wukong-gitlog-cli 0.0.9 → 0.0.10

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 CHANGED
@@ -2,6 +2,14 @@
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.10](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v0.0.9...v0.0.10) (2025-11-28)
6
+
7
+
8
+ ### Features
9
+
10
+ * 🎸 each hour percent ([4f1a7e7](https://github.com/tomatobybike/wukong-gitlog-cli/commit/4f1a7e75a1c0d83c92aabf42e97932421c349b8d))
11
+ * 🎸 hourline ([9035ecb](https://github.com/tomatobybike/wukong-gitlog-cli/commit/9035ecb33295fa5f4ff64bbd9b9281c543247683))
12
+
5
13
  ### [0.0.9](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v0.0.8...v0.0.9) (2025-11-28)
6
14
 
7
15
  ### [0.0.8](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v0.0.7...v0.0.8) (2025-11-28)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wukong-gitlog-cli",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "description": "Advanced Git commit log exporter with Excel/JSON/TXT output, grouping, stats and CLI.",
5
5
  "keywords": [
6
6
  "git",
package/src/overtime.mjs CHANGED
@@ -64,6 +64,8 @@ export function analyzeOvertime(records, opts = {}) {
64
64
  hd.init('CN');
65
65
  }
66
66
 
67
+ // 新增:每小时分布统计
68
+ const hourlyCommits = Array(24).fill(0);
67
69
  records.forEach((r) => {
68
70
  const dt = parseCommitDate(r.date);
69
71
  if (!dt || !dt.isValid()) return; // skip
@@ -72,6 +74,12 @@ export function analyzeOvertime(records, opts = {}) {
72
74
  const isHoliday = !!hd.isHoliday(dt.toDate());
73
75
  const isNonWork = isWeekend(dt) || isHoliday;
74
76
 
77
+ // 每小时分布
78
+ const hour = dt.hour();
79
+ if (hour >= 0 && hour < 24) {
80
+ hourlyCommits[hour]++;
81
+ }
82
+
75
83
  if (outside) {
76
84
  outsideWorkCount++;
77
85
  // 记录最新的加班提交
@@ -134,6 +142,8 @@ export function analyzeOvertime(records, opts = {}) {
134
142
  // sort perAuthor by outsideWorkRate desc
135
143
  perAuthor.sort((a, b) => b.outsideWorkRate - a.outsideWorkRate || b.total - a.total);
136
144
 
145
+ // 新增:每小时分布百分比
146
+ const hourlyPercent = hourlyCommits.map(v => total ? +(v / total).toFixed(3) : 0);
137
147
  return {
138
148
  total,
139
149
  outsideWorkCount,
@@ -148,16 +158,18 @@ export function analyzeOvertime(records, opts = {}) {
148
158
  latestOutsideCommit: latestOutsideCommit || null,
149
159
  startHour,
150
160
  endHour,
151
- lunchStart,
152
- lunchEnd,
153
- country,
154
- holidayCount,
155
- holidayRate: total ? +(holidayCount / total).toFixed(3) : 0,
161
+ lunchStart,
162
+ lunchEnd,
163
+ country,
164
+ holidayCount,
165
+ holidayRate: total ? +(holidayCount / total).toFixed(3) : 0,
166
+ hourlyCommits,
167
+ hourlyPercent,
156
168
  };
157
169
  }
158
170
 
159
171
  export function renderOvertimeText(stats) {
160
- const { total, outsideWorkCount, nonWorkdayCount, holidayCount, outsideWorkRate, nonWorkdayRate, holidayRate, perAuthor, startHour, endHour, lunchStart, lunchEnd, country } = stats;
172
+ const { total, outsideWorkCount, nonWorkdayCount, holidayCount, outsideWorkRate, nonWorkdayRate, holidayRate, perAuthor, startHour, endHour, lunchStart, lunchEnd, country, hourlyCommits = [], hourlyPercent = [] } = stats;
161
173
  const { startCommit, endCommit, latestCommit, latestOutsideCommit } = stats;
162
174
  const lines = [];
163
175
 
@@ -206,6 +218,18 @@ export function renderOvertimeText(stats) {
206
218
  lines.push(`下班时间(工作时间外)提交数:${outsideWorkCount},占比:${(outsideWorkRate * 100).toFixed(1)}%`);
207
219
  lines.push(`非工作日(周末)提交数:${nonWorkdayCount},占比:${(nonWorkdayRate * 100).toFixed(1)}%`);
208
220
  lines.push('');
221
+ lines.push('每小时分布(提交数/占比):');
222
+ const hourLine = ' Hour | Count | Percent';
223
+ lines.push(hourLine);
224
+ lines.push(' -----|-------|--------');
225
+ for (let h = 0; h < 24; h++) {
226
+ const cnt = hourlyCommits[h] || 0;
227
+ if (cnt > 0) {
228
+ const pct = hourlyPercent[h] ? (hourlyPercent[h] * 100).toFixed(1) : '0.0';
229
+ lines.push(` ${String(h).padStart(2, '0')} | ${String(cnt).padStart(5, ' ')} | ${pct.padStart(6, ' ')}%`);
230
+ }
231
+ }
232
+ lines.push('');
209
233
  lines.push('按人员统计:');
210
234
  // header
211
235
  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 +248,7 @@ export function renderOvertimeText(stats) {
224
248
  }
225
249
 
226
250
  export function renderOvertimeTab(stats) {
227
- const { total, outsideWorkCount, nonWorkdayCount, holidayCount, outsideWorkRate, nonWorkdayRate, holidayRate, perAuthor, startHour, endHour, lunchStart, lunchEnd, country } = stats;
251
+ const { total, outsideWorkCount, nonWorkdayCount, holidayCount, outsideWorkRate, nonWorkdayRate, holidayRate, perAuthor, startHour, endHour, lunchStart, lunchEnd, country, hourlyCommits = [], hourlyPercent = [] } = stats;
228
252
  const { startCommit, endCommit, latestCommit, latestOutsideCommit } = stats;
229
253
  const rows = [];
230
254
  rows.push(`总提交数:\t${total}`);
@@ -236,6 +260,16 @@ export function renderOvertimeTab(stats) {
236
260
  rows.push(`下班时间(工作时间外)提交数:\t${outsideWorkCount}\t占比:\t${(outsideWorkRate * 100).toFixed(1)}%`);
237
261
  rows.push(`非工作日(周末)提交数:\t${nonWorkdayCount}\t占比:\t${(nonWorkdayRate * 100).toFixed(1)}%`);
238
262
  rows.push('');
263
+ rows.push('每小时分布(提交数/占比):');
264
+ rows.push(['Hour', 'Count', 'Percent'].join('\t'));
265
+ for (let h = 0; h < 24; h++) {
266
+ const cnt = hourlyCommits[h] || 0;
267
+ if (cnt > 0) {
268
+ const pct = hourlyPercent[h] ? (hourlyPercent[h] * 100).toFixed(1) : '0.0';
269
+ rows.push([String(h).padStart(2, '0'), cnt, `${pct}%`].join('\t'));
270
+ }
271
+ }
272
+ rows.push('');
239
273
  rows.push(['Name', '总数', '下班外数', '下班外占比', '非工作日数', '非工作日占比', '假日数', '假日占比'].join('\t'));
240
274
  perAuthor.forEach((p) => {
241
275
  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 +286,22 @@ function escapeCsv(v) {
252
286
  }
253
287
 
254
288
  export function renderOvertimeCsv(stats) {
255
- const { perAuthor, latestOutsideCommit, country } = stats;
289
+ const { perAuthor, latestOutsideCommit, country, hourlyCommits = [], hourlyPercent = [], total = 0 } = stats;
256
290
  const rows = [];
257
291
  if (latestOutsideCommit) {
258
292
  rows.push(`# 加班最晚的一次提交,Hash,Author,Date,Message`);
259
293
  rows.push(`# ,${escapeCsv(latestOutsideCommit.hash)},${escapeCsv(latestOutsideCommit.author)},${escapeCsv(formatDateForCountry(latestOutsideCommit.date, country))},${escapeCsv(latestOutsideCommit.message)}`);
260
294
  }
295
+ rows.push('');
296
+ rows.push('Hour,Count,Percent');
297
+ for (let h = 0; h < 24; h++) {
298
+ const cnt = hourlyCommits[h] || 0;
299
+ if (cnt > 0) {
300
+ const pct = hourlyPercent[h] ? (hourlyPercent[h] * 100).toFixed(1) : '0.0';
301
+ rows.push(`${h.toString().padStart(2, '0')},${cnt},${pct}%`);
302
+ }
303
+ }
304
+ rows.push('');
261
305
  rows.push('Name,Total,OutsideCount,OutsideRate,NonWorkdayCount,NonWorkdayRate,HolidayCount,HolidayRate');
262
306
  perAuthor.forEach((p) => {
263
307
  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)}%`);