repo-wrapped 0.0.2 → 0.0.4
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/dist/commands/generate.js +104 -95
- package/dist/constants/chronotypes.js +23 -23
- package/dist/constants/colors.js +18 -18
- package/dist/constants/index.js +18 -18
- package/dist/formatters/index.js +17 -17
- package/dist/formatters/timeFormatter.js +28 -29
- package/dist/generators/html/templates/achievementsSection.js +42 -43
- package/dist/generators/html/templates/commitQualitySection.js +25 -26
- package/dist/generators/html/templates/contributionGraph.js +47 -48
- package/dist/generators/html/templates/impactSection.js +19 -20
- package/dist/generators/html/templates/knowledgeSection.js +86 -87
- package/dist/generators/html/templates/streakSection.js +8 -9
- package/dist/generators/html/templates/timePatternsSection.js +45 -46
- package/dist/generators/html/utils/colorUtils.js +21 -21
- package/dist/generators/html/utils/commitMapBuilder.js +23 -24
- package/dist/generators/html/utils/dateRangeCalculator.js +56 -57
- package/dist/generators/html/utils/developerStatsCalculator.js +28 -29
- package/dist/generators/html/utils/scriptLoader.js +15 -16
- package/dist/generators/html/utils/styleLoader.js +17 -18
- package/dist/generators/html/utils/weekGrouper.js +27 -28
- package/dist/index.js +99 -77
- package/dist/types/index.js +2 -2
- package/dist/utils/achievementDefinitions.js +433 -433
- package/dist/utils/achievementEngine.js +169 -170
- package/dist/utils/commitQualityAnalyzer.js +367 -368
- package/dist/utils/fileHotspotAnalyzer.js +269 -270
- package/dist/utils/gitParser.js +136 -125
- package/dist/utils/htmlGenerator.js +232 -233
- package/dist/utils/impactAnalyzer.js +247 -248
- package/dist/utils/knowledgeDistributionAnalyzer.js +373 -374
- package/dist/utils/matrixGenerator.js +349 -350
- package/dist/utils/slideGenerator.js +170 -171
- package/dist/utils/streakCalculator.js +134 -135
- package/dist/utils/timePatternAnalyzer.js +304 -305
- package/dist/utils/wrappedDisplay.js +124 -115
- package/dist/utils/wrappedGenerator.js +376 -377
- package/dist/utils/wrappedHtmlGenerator.js +105 -106
- package/package.json +10 -10
|
@@ -1,305 +1,304 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
//
|
|
61
|
-
const
|
|
62
|
-
//
|
|
63
|
-
const
|
|
64
|
-
//
|
|
65
|
-
const
|
|
66
|
-
//
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
.
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
let
|
|
157
|
-
let
|
|
158
|
-
let
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const
|
|
173
|
-
const
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
//
|
|
202
|
-
//
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
indicators.push('✅
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
const
|
|
232
|
-
const
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
//
|
|
236
|
-
// stdDev <
|
|
237
|
-
// stdDev <
|
|
238
|
-
// stdDev
|
|
239
|
-
|
|
240
|
-
let
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
'
|
|
290
|
-
'
|
|
291
|
-
'
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
function getChronotypeDescription(chronotype) {
|
|
297
|
-
const descriptions = {
|
|
298
|
-
'early-bird': "You're most productive in the morning. Consider scheduling important work before lunch.",
|
|
299
|
-
'night-owl': "You're most productive in the evening. Night time is when you shine!",
|
|
300
|
-
'balanced': "You maintain a well-balanced coding schedule throughout the day.",
|
|
301
|
-
'vampire': "⚠️ Many late-night commits detected. Consider adjusting your schedule for better work-life balance."
|
|
302
|
-
};
|
|
303
|
-
return descriptions[chronotype] || '';
|
|
304
|
-
}
|
|
305
|
-
exports.getChronotypeDescription = getChronotypeDescription;
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.analyzeTimePatterns = analyzeTimePatterns;
|
|
4
|
+
exports.getChronotypeLabel = getChronotypeLabel;
|
|
5
|
+
exports.getChronotypeDescription = getChronotypeDescription;
|
|
6
|
+
const date_fns_1 = require("date-fns");
|
|
7
|
+
function analyzeTimePatterns(commits, startDate, endDate) {
|
|
8
|
+
if (commits.length === 0) {
|
|
9
|
+
return getEmptyPattern();
|
|
10
|
+
}
|
|
11
|
+
// Filter commits within the date range
|
|
12
|
+
const filteredCommits = commits.filter(c => {
|
|
13
|
+
const commitDate = new Date(c.date);
|
|
14
|
+
return commitDate >= startDate && commitDate <= endDate;
|
|
15
|
+
});
|
|
16
|
+
if (filteredCommits.length === 0) {
|
|
17
|
+
return getEmptyPattern();
|
|
18
|
+
}
|
|
19
|
+
// 1. Hourly distribution
|
|
20
|
+
const hourlyMap = new Map();
|
|
21
|
+
for (let i = 0; i < 24; i++) {
|
|
22
|
+
hourlyMap.set(i, 0);
|
|
23
|
+
}
|
|
24
|
+
filteredCommits.forEach(commit => {
|
|
25
|
+
const hour = (0, date_fns_1.getHours)(new Date(commit.date));
|
|
26
|
+
hourlyMap.set(hour, (hourlyMap.get(hour) || 0) + 1);
|
|
27
|
+
});
|
|
28
|
+
const hourly = Array.from(hourlyMap.entries()).map(([hour, count]) => ({
|
|
29
|
+
hour,
|
|
30
|
+
commitCount: count,
|
|
31
|
+
percentage: (count / filteredCommits.length) * 100
|
|
32
|
+
}));
|
|
33
|
+
// 2. Daily distribution (day of week)
|
|
34
|
+
const dayMap = new Map();
|
|
35
|
+
for (let i = 0; i < 7; i++) {
|
|
36
|
+
dayMap.set(i, []);
|
|
37
|
+
}
|
|
38
|
+
filteredCommits.forEach(commit => {
|
|
39
|
+
const dayIndex = (0, date_fns_1.getDay)(new Date(commit.date));
|
|
40
|
+
dayMap.get(dayIndex).push(1);
|
|
41
|
+
});
|
|
42
|
+
const dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
|
43
|
+
const dailyAverage = Array.from(dayMap.entries()).map(([dayIndex, commits]) => ({
|
|
44
|
+
dayOfWeek: dayNames[dayIndex],
|
|
45
|
+
commitCount: commits.length,
|
|
46
|
+
avgCommitsPerDay: commits.length / Math.ceil((endDate.getTime() - startDate.getTime()) / (7 * 24 * 60 * 60 * 1000))
|
|
47
|
+
}));
|
|
48
|
+
// 3. Peak hour
|
|
49
|
+
const peakHourEntry = hourly.reduce((max, curr) => curr.commitCount > max.commitCount ? curr : max);
|
|
50
|
+
const peakHour = {
|
|
51
|
+
hour: peakHourEntry.hour,
|
|
52
|
+
commitCount: peakHourEntry.commitCount
|
|
53
|
+
};
|
|
54
|
+
// 4. Peak day
|
|
55
|
+
const peakDayEntry = dailyAverage.reduce((max, curr) => curr.commitCount > max.commitCount ? curr : max);
|
|
56
|
+
const peakDay = {
|
|
57
|
+
dayOfWeek: peakDayEntry.dayOfWeek,
|
|
58
|
+
avgCommits: peakDayEntry.avgCommitsPerDay
|
|
59
|
+
};
|
|
60
|
+
// 5. Chronotype detection
|
|
61
|
+
const chronotypeData = getChronotype(hourly);
|
|
62
|
+
// 6. Work-life balance
|
|
63
|
+
const workLifeBalance = calculateWorkLifeBalance(dailyAverage);
|
|
64
|
+
// 7. Timing stats
|
|
65
|
+
const timingStats = calculateTimingStats(filteredCommits);
|
|
66
|
+
// 8. Burnout risk
|
|
67
|
+
const burnoutRisk = detectBurnoutRisk(hourly, dailyAverage, filteredCommits);
|
|
68
|
+
// 9. Consistency score
|
|
69
|
+
const consistency = calculateConsistency(filteredCommits);
|
|
70
|
+
return {
|
|
71
|
+
hourly,
|
|
72
|
+
dailyAverage,
|
|
73
|
+
chronotype: chronotypeData.type,
|
|
74
|
+
chronotypeConfidence: chronotypeData.confidence,
|
|
75
|
+
peakHour,
|
|
76
|
+
peakDay,
|
|
77
|
+
workLifeBalance,
|
|
78
|
+
timingStats,
|
|
79
|
+
burnoutRisk,
|
|
80
|
+
consistency
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function getChronotype(hourly) {
|
|
84
|
+
const totalCommits = hourly.reduce((sum, h) => sum + h.commitCount, 0);
|
|
85
|
+
if (totalCommits === 0) {
|
|
86
|
+
return { type: 'balanced', confidence: 0 };
|
|
87
|
+
}
|
|
88
|
+
// Calculate time period percentages
|
|
89
|
+
const morningCommits = hourly.filter(h => h.hour >= 6 && h.hour < 12).reduce((sum, h) => sum + h.commitCount, 0);
|
|
90
|
+
const eveningCommits = hourly.filter(h => h.hour >= 18 && h.hour < 24).reduce((sum, h) => sum + h.commitCount, 0);
|
|
91
|
+
const nightCommits = hourly.filter(h => h.hour >= 0 && h.hour < 6).reduce((sum, h) => sum + h.commitCount, 0);
|
|
92
|
+
const morningPct = (morningCommits / totalCommits) * 100;
|
|
93
|
+
const eveningPct = (eveningCommits / totalCommits) * 100;
|
|
94
|
+
const nightPct = (nightCommits / totalCommits) * 100;
|
|
95
|
+
// Vampire: 30%+ commits between midnight-6am
|
|
96
|
+
if (nightPct >= 30) {
|
|
97
|
+
return { type: 'vampire', confidence: nightPct };
|
|
98
|
+
}
|
|
99
|
+
// Early Bird: 60%+ commits before 12pm
|
|
100
|
+
if (morningPct >= 60) {
|
|
101
|
+
return { type: 'early-bird', confidence: morningPct };
|
|
102
|
+
}
|
|
103
|
+
// Night Owl: 60%+ commits after 6pm
|
|
104
|
+
if (eveningPct >= 60) {
|
|
105
|
+
return { type: 'night-owl', confidence: eveningPct };
|
|
106
|
+
}
|
|
107
|
+
// Balanced: Even distribution
|
|
108
|
+
return { type: 'balanced', confidence: 100 - Math.max(morningPct, eveningPct, nightPct) };
|
|
109
|
+
}
|
|
110
|
+
function calculateWorkLifeBalance(dailyAverage) {
|
|
111
|
+
const weekdayCommits = dailyAverage
|
|
112
|
+
.filter(d => !['Saturday', 'Sunday'].includes(d.dayOfWeek))
|
|
113
|
+
.reduce((sum, d) => sum + d.commitCount, 0);
|
|
114
|
+
const weekendCommits = dailyAverage
|
|
115
|
+
.filter(d => ['Saturday', 'Sunday'].includes(d.dayOfWeek))
|
|
116
|
+
.reduce((sum, d) => sum + d.commitCount, 0);
|
|
117
|
+
const totalCommits = weekdayCommits + weekendCommits;
|
|
118
|
+
const ratio = totalCommits > 0 ? weekdayCommits / weekendCommits : 0;
|
|
119
|
+
// Score: 5 stars = ratio > 10 (very little weekend work)
|
|
120
|
+
// 1 star = ratio < 2 (too much weekend work)
|
|
121
|
+
let score = 5;
|
|
122
|
+
if (ratio < 2)
|
|
123
|
+
score = 1;
|
|
124
|
+
else if (ratio < 4)
|
|
125
|
+
score = 2;
|
|
126
|
+
else if (ratio < 6)
|
|
127
|
+
score = 3;
|
|
128
|
+
else if (ratio < 10)
|
|
129
|
+
score = 4;
|
|
130
|
+
return {
|
|
131
|
+
weekdayCommits,
|
|
132
|
+
weekendCommits,
|
|
133
|
+
ratio,
|
|
134
|
+
score
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function calculateTimingStats(commits) {
|
|
138
|
+
if (commits.length === 0) {
|
|
139
|
+
return {
|
|
140
|
+
avgFirstCommit: '00:00',
|
|
141
|
+
avgLastCommit: '00:00',
|
|
142
|
+
avgSessionLength: 0,
|
|
143
|
+
longestSession: { date: new Date(), hours: 0 }
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
// Group commits by date
|
|
147
|
+
const commitsByDate = new Map();
|
|
148
|
+
commits.forEach(commit => {
|
|
149
|
+
const dateKey = (0, date_fns_1.format)(new Date(commit.date), 'yyyy-MM-dd');
|
|
150
|
+
if (!commitsByDate.has(dateKey)) {
|
|
151
|
+
commitsByDate.set(dateKey, []);
|
|
152
|
+
}
|
|
153
|
+
commitsByDate.get(dateKey).push(new Date(commit.date));
|
|
154
|
+
});
|
|
155
|
+
// Calculate first/last commit times and session lengths
|
|
156
|
+
let totalFirstHour = 0;
|
|
157
|
+
let totalLastHour = 0;
|
|
158
|
+
let totalSessionHours = 0;
|
|
159
|
+
let longestSession = { date: new Date(), hours: 0 };
|
|
160
|
+
commitsByDate.forEach((times, dateKey) => {
|
|
161
|
+
times.sort((a, b) => a.getTime() - b.getTime());
|
|
162
|
+
const firstCommit = times[0];
|
|
163
|
+
const lastCommit = times[times.length - 1];
|
|
164
|
+
totalFirstHour += firstCommit.getHours() + (firstCommit.getMinutes() / 60);
|
|
165
|
+
totalLastHour += lastCommit.getHours() + (lastCommit.getMinutes() / 60);
|
|
166
|
+
const sessionLength = (0, date_fns_1.differenceInHours)(lastCommit, firstCommit);
|
|
167
|
+
totalSessionHours += sessionLength;
|
|
168
|
+
if (sessionLength > longestSession.hours) {
|
|
169
|
+
longestSession = { date: (0, date_fns_1.parse)(dateKey, 'yyyy-MM-dd', new Date()), hours: sessionLength };
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
const daysWithCommits = commitsByDate.size;
|
|
173
|
+
const avgFirstHour = totalFirstHour / daysWithCommits;
|
|
174
|
+
const avgLastHour = totalLastHour / daysWithCommits;
|
|
175
|
+
const formatTime = (decimalHour) => {
|
|
176
|
+
const hour = Math.floor(decimalHour);
|
|
177
|
+
const minute = Math.floor((decimalHour - hour) * 60);
|
|
178
|
+
return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
|
|
179
|
+
};
|
|
180
|
+
return {
|
|
181
|
+
avgFirstCommit: formatTime(avgFirstHour),
|
|
182
|
+
avgLastCommit: formatTime(avgLastHour),
|
|
183
|
+
avgSessionLength: totalSessionHours / daysWithCommits,
|
|
184
|
+
longestSession
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function detectBurnoutRisk(hourly, dailyAverage, commits) {
|
|
188
|
+
const indicators = [];
|
|
189
|
+
let riskScore = 0;
|
|
190
|
+
// Check for late-night commits (after 11 PM)
|
|
191
|
+
const lateNightCommits = hourly.filter(h => h.hour >= 23).reduce((sum, h) => sum + h.commitCount, 0);
|
|
192
|
+
const veryLateCommits = hourly.filter(h => h.hour >= 0 && h.hour < 6).reduce((sum, h) => sum + h.commitCount, 0);
|
|
193
|
+
if (lateNightCommits > 15) {
|
|
194
|
+
indicators.push(`${lateNightCommits} late-night commits (after 11 PM)`);
|
|
195
|
+
riskScore += 20;
|
|
196
|
+
}
|
|
197
|
+
if (veryLateCommits > 10) {
|
|
198
|
+
indicators.push(`${veryLateCommits} commits between midnight-6am`);
|
|
199
|
+
riskScore += 30;
|
|
200
|
+
}
|
|
201
|
+
// Weekend work is not counted as a burnout indicator since it's ambiguous
|
|
202
|
+
// (could be hobby projects vs. overwork) - focusing on late night patterns instead
|
|
203
|
+
// Check for uneven distribution (working all hours)
|
|
204
|
+
const activeHours = hourly.filter(h => h.commitCount > 0).length;
|
|
205
|
+
if (activeHours > 16) {
|
|
206
|
+
indicators.push('Commits spread across too many hours');
|
|
207
|
+
riskScore += 15;
|
|
208
|
+
}
|
|
209
|
+
// Determine risk level
|
|
210
|
+
let level = 'low';
|
|
211
|
+
if (riskScore >= 50)
|
|
212
|
+
level = 'high';
|
|
213
|
+
else if (riskScore >= 25)
|
|
214
|
+
level = 'medium';
|
|
215
|
+
// Add positive indicators if low risk
|
|
216
|
+
if (riskScore < 25) {
|
|
217
|
+
indicators.push('✅ Regular commit times (±2 hour variance)');
|
|
218
|
+
indicators.push('✅ Healthy work-life balance');
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
level,
|
|
222
|
+
indicators,
|
|
223
|
+
score: Math.min(riskScore, 100)
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function calculateConsistency(commits) {
|
|
227
|
+
if (commits.length === 0) {
|
|
228
|
+
return { score: 0, variance: 0, regularity: 'chaotic' };
|
|
229
|
+
}
|
|
230
|
+
// Calculate variance in commit times (hour of day)
|
|
231
|
+
const hours = commits.map(c => (0, date_fns_1.getHours)(new Date(c.date)));
|
|
232
|
+
const avgHour = hours.reduce((sum, h) => sum + h, 0) / hours.length;
|
|
233
|
+
const variance = hours.reduce((sum, h) => sum + Math.pow(h - avgHour, 2), 0) / hours.length;
|
|
234
|
+
const stdDev = Math.sqrt(variance);
|
|
235
|
+
// Score based on standard deviation (lower = more consistent)
|
|
236
|
+
// stdDev < 2: very consistent (same 2-hour window)
|
|
237
|
+
// stdDev < 4: consistent
|
|
238
|
+
// stdDev < 6: irregular
|
|
239
|
+
// stdDev >= 6: chaotic
|
|
240
|
+
let score = 10;
|
|
241
|
+
let regularity = 'very-consistent';
|
|
242
|
+
if (stdDev >= 6) {
|
|
243
|
+
score = Math.max(0, 10 - stdDev);
|
|
244
|
+
regularity = 'chaotic';
|
|
245
|
+
}
|
|
246
|
+
else if (stdDev >= 4) {
|
|
247
|
+
score = 10 - stdDev;
|
|
248
|
+
regularity = 'irregular';
|
|
249
|
+
}
|
|
250
|
+
else if (stdDev >= 2) {
|
|
251
|
+
score = 10 - (stdDev / 2);
|
|
252
|
+
regularity = 'consistent';
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
score = 10 - (stdDev / 4);
|
|
256
|
+
regularity = 'very-consistent';
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
score: Math.max(0, Math.min(10, score)),
|
|
260
|
+
variance: stdDev,
|
|
261
|
+
regularity
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function getEmptyPattern() {
|
|
265
|
+
return {
|
|
266
|
+
hourly: Array.from({ length: 24 }, (_, i) => ({ hour: i, commitCount: 0, percentage: 0 })),
|
|
267
|
+
dailyAverage: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'].map(day => ({
|
|
268
|
+
dayOfWeek: day,
|
|
269
|
+
commitCount: 0,
|
|
270
|
+
avgCommitsPerDay: 0
|
|
271
|
+
})),
|
|
272
|
+
chronotype: 'balanced',
|
|
273
|
+
chronotypeConfidence: 0,
|
|
274
|
+
peakHour: { hour: 0, commitCount: 0 },
|
|
275
|
+
peakDay: { dayOfWeek: 'Monday', avgCommits: 0 },
|
|
276
|
+
workLifeBalance: { weekdayCommits: 0, weekendCommits: 0, ratio: 0, score: 5 },
|
|
277
|
+
timingStats: {
|
|
278
|
+
avgFirstCommit: '00:00',
|
|
279
|
+
avgLastCommit: '00:00',
|
|
280
|
+
avgSessionLength: 0,
|
|
281
|
+
longestSession: { date: new Date(), hours: 0 }
|
|
282
|
+
},
|
|
283
|
+
burnoutRisk: { level: 'low', indicators: [], score: 0 },
|
|
284
|
+
consistency: { score: 0, variance: 0, regularity: 'chaotic' }
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function getChronotypeLabel(chronotype) {
|
|
288
|
+
const labels = {
|
|
289
|
+
'early-bird': '🐦 Early Bird',
|
|
290
|
+
'night-owl': '🦉 Night Owl',
|
|
291
|
+
'balanced': '⚖️ Balanced',
|
|
292
|
+
'vampire': '🧛 Vampire'
|
|
293
|
+
};
|
|
294
|
+
return labels[chronotype] || chronotype;
|
|
295
|
+
}
|
|
296
|
+
function getChronotypeDescription(chronotype) {
|
|
297
|
+
const descriptions = {
|
|
298
|
+
'early-bird': "You're most productive in the morning. Consider scheduling important work before lunch.",
|
|
299
|
+
'night-owl': "You're most productive in the evening. Night time is when you shine!",
|
|
300
|
+
'balanced': "You maintain a well-balanced coding schedule throughout the day.",
|
|
301
|
+
'vampire': "⚠️ Many late-night commits detected. Consider adjusting your schedule for better work-life balance."
|
|
302
|
+
};
|
|
303
|
+
return descriptions[chronotype] || '';
|
|
304
|
+
}
|