wukong-gitlog-cli 0.0.9 → 0.0.11
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.
- package/CHANGELOG.md +15 -0
- package/package.json +1 -1
- package/src/overtime.mjs +61 -8
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [0.0.11](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v0.0.10...v0.0.11) (2025-11-28)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* 🎸 each hour commit ([029ff72](https://github.com/tomatobybike/wukong-gitlog-cli/commit/029ff72636a47bcb5e77b7fb5acf1ca31d9cf9f3))
|
|
11
|
+
|
|
12
|
+
### [0.0.10](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v0.0.9...v0.0.10) (2025-11-28)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* 🎸 each hour percent ([4f1a7e7](https://github.com/tomatobybike/wukong-gitlog-cli/commit/4f1a7e75a1c0d83c92aabf42e97932421c349b8d))
|
|
18
|
+
* 🎸 hourline ([9035ecb](https://github.com/tomatobybike/wukong-gitlog-cli/commit/9035ecb33295fa5f4ff64bbd9b9281c543247683))
|
|
19
|
+
|
|
5
20
|
### [0.0.9](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v0.0.8...v0.0.9) (2025-11-28)
|
|
6
21
|
|
|
7
22
|
### [0.0.8](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v0.0.7...v0.0.8) (2025-11-28)
|
package/package.json
CHANGED
package/src/overtime.mjs
CHANGED
|
@@ -64,6 +64,10 @@ export function analyzeOvertime(records, opts = {}) {
|
|
|
64
64
|
hd.init('CN');
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
// 新增:每小时分布统计(全部提交)
|
|
68
|
+
const hourlyCommits = Array(24).fill(0);
|
|
69
|
+
// 新增:每小时分布统计(仅加班提交)
|
|
70
|
+
const hourlyOvertimeCommits = Array(24).fill(0);
|
|
67
71
|
records.forEach((r) => {
|
|
68
72
|
const dt = parseCommitDate(r.date);
|
|
69
73
|
if (!dt || !dt.isValid()) return; // skip
|
|
@@ -72,6 +76,15 @@ export function analyzeOvertime(records, opts = {}) {
|
|
|
72
76
|
const isHoliday = !!hd.isHoliday(dt.toDate());
|
|
73
77
|
const isNonWork = isWeekend(dt) || isHoliday;
|
|
74
78
|
|
|
79
|
+
// 每小时分布(全部提交)
|
|
80
|
+
const hour = dt.hour();
|
|
81
|
+
if (hour >= 0 && hour < 24) {
|
|
82
|
+
hourlyCommits[hour]++;
|
|
83
|
+
if (outside) {
|
|
84
|
+
hourlyOvertimeCommits[hour]++;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
75
88
|
if (outside) {
|
|
76
89
|
outsideWorkCount++;
|
|
77
90
|
// 记录最新的加班提交
|
|
@@ -134,6 +147,10 @@ export function analyzeOvertime(records, opts = {}) {
|
|
|
134
147
|
// sort perAuthor by outsideWorkRate desc
|
|
135
148
|
perAuthor.sort((a, b) => b.outsideWorkRate - a.outsideWorkRate || b.total - a.total);
|
|
136
149
|
|
|
150
|
+
// 新增:每小时分布百分比(全部提交)
|
|
151
|
+
const hourlyPercent = hourlyCommits.map(v => total ? +(v / total).toFixed(3) : 0);
|
|
152
|
+
// 新增:每小时分布百分比(仅加班提交)
|
|
153
|
+
const hourlyOvertimePercent = hourlyOvertimeCommits.map(v => total ? +(v / total).toFixed(3) : 0);
|
|
137
154
|
return {
|
|
138
155
|
total,
|
|
139
156
|
outsideWorkCount,
|
|
@@ -148,16 +165,20 @@ export function analyzeOvertime(records, opts = {}) {
|
|
|
148
165
|
latestOutsideCommit: latestOutsideCommit || null,
|
|
149
166
|
startHour,
|
|
150
167
|
endHour,
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
168
|
+
lunchStart,
|
|
169
|
+
lunchEnd,
|
|
170
|
+
country,
|
|
171
|
+
holidayCount,
|
|
172
|
+
holidayRate: total ? +(holidayCount / total).toFixed(3) : 0,
|
|
173
|
+
hourlyCommits,
|
|
174
|
+
hourlyPercent,
|
|
175
|
+
hourlyOvertimeCommits,
|
|
176
|
+
hourlyOvertimePercent,
|
|
156
177
|
};
|
|
157
178
|
}
|
|
158
179
|
|
|
159
180
|
export function renderOvertimeText(stats) {
|
|
160
|
-
const { total, outsideWorkCount, nonWorkdayCount, holidayCount, outsideWorkRate, nonWorkdayRate, holidayRate, perAuthor, startHour, endHour, lunchStart, lunchEnd, country } = stats;
|
|
181
|
+
const { total, outsideWorkCount, nonWorkdayCount, holidayCount, outsideWorkRate, nonWorkdayRate, holidayRate, perAuthor, startHour, endHour, lunchStart, lunchEnd, country, hourlyOvertimeCommits = [], hourlyOvertimePercent = [] } = stats;
|
|
161
182
|
const { startCommit, endCommit, latestCommit, latestOutsideCommit } = stats;
|
|
162
183
|
const lines = [];
|
|
163
184
|
|
|
@@ -206,6 +227,18 @@ export function renderOvertimeText(stats) {
|
|
|
206
227
|
lines.push(`下班时间(工作时间外)提交数:${outsideWorkCount},占比:${(outsideWorkRate * 100).toFixed(1)}%`);
|
|
207
228
|
lines.push(`非工作日(周末)提交数:${nonWorkdayCount},占比:${(nonWorkdayRate * 100).toFixed(1)}%`);
|
|
208
229
|
lines.push('');
|
|
230
|
+
lines.push('每小时加班分布(工作时间外提交数/占比):');
|
|
231
|
+
const hourLine = ' Hour | OvertimeCount | Percent';
|
|
232
|
+
lines.push(hourLine);
|
|
233
|
+
lines.push(' -----|--------------|--------');
|
|
234
|
+
for (let h = 0; h < 24; h++) {
|
|
235
|
+
const cnt = hourlyOvertimeCommits[h] || 0;
|
|
236
|
+
if (cnt > 0) {
|
|
237
|
+
const pct = hourlyOvertimePercent[h] ? (hourlyOvertimePercent[h] * 100).toFixed(1) : '0.0';
|
|
238
|
+
lines.push(` ${String(h).padStart(2, '0')} | ${String(cnt).padStart(12, ' ')} | ${pct.padStart(6, ' ')}%`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
lines.push('');
|
|
209
242
|
lines.push('按人员统计:');
|
|
210
243
|
// header
|
|
211
244
|
const header = ` ${padDisplayEnd('Name', cols.name)} | ${padDisplayStart('总数', cols.total)} | ${padDisplayStart('下班外数', cols.outside)} | ${padDisplayStart('下班外占比', cols.outsideRate)} | ${padDisplayStart('非工作日数', cols.nonWork)} | ${padDisplayStart('非工作日占比', cols.nonWorkRate)} | ${padDisplayStart('假日数', cols.holiday)} | ${padDisplayStart('假日占比', cols.holidayRate)}`;
|
|
@@ -224,7 +257,7 @@ export function renderOvertimeText(stats) {
|
|
|
224
257
|
}
|
|
225
258
|
|
|
226
259
|
export function renderOvertimeTab(stats) {
|
|
227
|
-
const { total, outsideWorkCount, nonWorkdayCount, holidayCount, outsideWorkRate, nonWorkdayRate, holidayRate, perAuthor, startHour, endHour, lunchStart, lunchEnd, country } = stats;
|
|
260
|
+
const { total, outsideWorkCount, nonWorkdayCount, holidayCount, outsideWorkRate, nonWorkdayRate, holidayRate, perAuthor, startHour, endHour, lunchStart, lunchEnd, country, hourlyOvertimeCommits = [], hourlyOvertimePercent = [] } = stats;
|
|
228
261
|
const { startCommit, endCommit, latestCommit, latestOutsideCommit } = stats;
|
|
229
262
|
const rows = [];
|
|
230
263
|
rows.push(`总提交数:\t${total}`);
|
|
@@ -236,6 +269,16 @@ export function renderOvertimeTab(stats) {
|
|
|
236
269
|
rows.push(`下班时间(工作时间外)提交数:\t${outsideWorkCount}\t占比:\t${(outsideWorkRate * 100).toFixed(1)}%`);
|
|
237
270
|
rows.push(`非工作日(周末)提交数:\t${nonWorkdayCount}\t占比:\t${(nonWorkdayRate * 100).toFixed(1)}%`);
|
|
238
271
|
rows.push('');
|
|
272
|
+
rows.push('每小时加班分布(工作时间外提交数/占比):');
|
|
273
|
+
rows.push(['Hour', 'OvertimeCount', 'Percent'].join('\t'));
|
|
274
|
+
for (let h = 0; h < 24; h++) {
|
|
275
|
+
const cnt = hourlyOvertimeCommits[h] || 0;
|
|
276
|
+
if (cnt > 0) {
|
|
277
|
+
const pct = hourlyOvertimePercent[h] ? (hourlyOvertimePercent[h] * 100).toFixed(1) : '0.0';
|
|
278
|
+
rows.push([String(h).padStart(2, '0'), cnt, `${pct}%`].join('\t'));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
rows.push('');
|
|
239
282
|
rows.push(['Name', '总数', '下班外数', '下班外占比', '非工作日数', '非工作日占比', '假日数', '假日占比'].join('\t'));
|
|
240
283
|
perAuthor.forEach((p) => {
|
|
241
284
|
rows.push([p.name || '-', p.total, p.outsideWorkCount, `${(p.outsideWorkRate * 100).toFixed(1)}%`, p.nonWorkdayCount, `${(p.nonWorkdayRate * 100).toFixed(1)}%`, p.holidayCount || 0, `${((p.holidayCount || 0) / p.total * 100).toFixed(1)}%`].join('\t'));
|
|
@@ -252,12 +295,22 @@ function escapeCsv(v) {
|
|
|
252
295
|
}
|
|
253
296
|
|
|
254
297
|
export function renderOvertimeCsv(stats) {
|
|
255
|
-
const { perAuthor, latestOutsideCommit, country } = stats;
|
|
298
|
+
const { perAuthor, latestOutsideCommit, country, hourlyOvertimeCommits = [], hourlyOvertimePercent = [], total = 0 } = stats;
|
|
256
299
|
const rows = [];
|
|
257
300
|
if (latestOutsideCommit) {
|
|
258
301
|
rows.push(`# 加班最晚的一次提交,Hash,Author,Date,Message`);
|
|
259
302
|
rows.push(`# ,${escapeCsv(latestOutsideCommit.hash)},${escapeCsv(latestOutsideCommit.author)},${escapeCsv(formatDateForCountry(latestOutsideCommit.date, country))},${escapeCsv(latestOutsideCommit.message)}`);
|
|
260
303
|
}
|
|
304
|
+
rows.push('');
|
|
305
|
+
rows.push('Hour,OvertimeCount,Percent');
|
|
306
|
+
for (let h = 0; h < 24; h++) {
|
|
307
|
+
const cnt = hourlyOvertimeCommits[h] || 0;
|
|
308
|
+
if (cnt > 0) {
|
|
309
|
+
const pct = hourlyOvertimePercent[h] ? (hourlyOvertimePercent[h] * 100).toFixed(1) : '0.0';
|
|
310
|
+
rows.push(`${h.toString().padStart(2, '0')},${cnt},${pct}%`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
rows.push('');
|
|
261
314
|
rows.push('Name,Total,OutsideCount,OutsideRate,NonWorkdayCount,NonWorkdayRate,HolidayCount,HolidayRate');
|
|
262
315
|
perAuthor.forEach((p) => {
|
|
263
316
|
rows.push(`${escapeCsv(p.name)},${p.total},${p.outsideWorkCount},${(p.outsideWorkRate * 100).toFixed(1)}%,${p.nonWorkdayCount},${(p.nonWorkdayRate * 100).toFixed(1)}%,${p.holidayCount || 0},${((p.holidayCount || 0) / p.total * 100).toFixed(1)}%`);
|