Skip to content

Commit ab028c7

Browse files
committed
realign & improve
1 parent c0de736 commit ab028c7

9 files changed

Lines changed: 1166 additions & 434 deletions

File tree

cmd/prcost/main.go

Lines changed: 77 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -226,16 +226,15 @@ func printHumanReadable(breakdown *cost.Breakdown, prURL string) {
226226
authorLabel += " (bot)"
227227
}
228228
fmt.Printf(" Author: %s • Open: %s\n", authorLabel, formatTimeUnit(breakdown.PRDuration))
229-
fmt.Printf(" Rate: %s/hr • Salary: %s • Benefits: %.1fx\n",
229+
fmt.Printf(" Rate: %s/hr • Benefits multiplier: %.1fx\n",
230230
formatCurrency(breakdown.HourlyRate),
231-
formatCurrency(breakdown.AnnualSalary),
232231
breakdown.BenefitsMultiplier)
233232
fmt.Println()
234233

235234
// Author Costs (skip entire section if no costs)
236235
if breakdown.Author.TotalCost > 0 {
237-
fmt.Println(" Development Cost")
238-
fmt.Println(" ────────────────")
236+
fmt.Println(" Development Costs")
237+
fmt.Println(" ────────────────")
239238
// Show development and adaptation separately (only if there are actual lines of code)
240239
if breakdown.Author.NewLines > 0 {
241240
fmt.Printf(" New Development %12s %d LOC • %s\n",
@@ -269,8 +268,8 @@ func printHumanReadable(breakdown *cost.Breakdown, prURL string) {
269268
totalParticipantHours += p.TotalHours
270269
}
271270

272-
fmt.Println(" Participant Cost")
273-
fmt.Println(" ────────────────")
271+
fmt.Println(" Participant Costs")
272+
fmt.Println(" ────────────────")
274273
for _, p := range breakdown.Participants {
275274
fmt.Printf(" %s\n", p.Actor)
276275
// Only show review activity if they reviewed (LOC-based)
@@ -295,84 +294,87 @@ func printHumanReadable(breakdown *cost.Breakdown, prURL string) {
295294
fmt.Println()
296295
}
297296

298-
// Merge Delay Costs
299-
fmt.Println(" Delay Costs")
300-
fmt.Println(" ───────────")
301-
if breakdown.DelayCostDetail.DeliveryDelayHours > 0 {
302-
if breakdown.DelayCapped {
303-
fmt.Printf(" Delivery %12s %s (capped)\n",
304-
formatCurrency(breakdown.DelayCostDetail.DeliveryDelayCost), formatTimeUnit(breakdown.DelayCostDetail.DeliveryDelayHours))
305-
} else {
306-
fmt.Printf(" Delivery %12s %s\n",
307-
formatCurrency(breakdown.DelayCostDetail.DeliveryDelayCost), formatTimeUnit(breakdown.DelayCostDetail.DeliveryDelayHours))
297+
// Delay and Future Costs - only show if there are any delay costs
298+
if breakdown.DelayCost > 0 {
299+
// Merge Delay Costs
300+
fmt.Println(" Delay Costs")
301+
fmt.Println(" ───────────")
302+
if breakdown.DelayCostDetail.DeliveryDelayHours > 0 {
303+
if breakdown.DelayCapped {
304+
fmt.Printf(" Delivery %12s %s (capped)\n",
305+
formatCurrency(breakdown.DelayCostDetail.DeliveryDelayCost), formatTimeUnit(breakdown.DelayCostDetail.DeliveryDelayHours))
306+
} else {
307+
fmt.Printf(" Delivery %12s %s\n",
308+
formatCurrency(breakdown.DelayCostDetail.DeliveryDelayCost), formatTimeUnit(breakdown.DelayCostDetail.DeliveryDelayHours))
309+
}
308310
}
309-
}
310311

311-
if breakdown.DelayCostDetail.CoordinationHours > 0 {
312-
if breakdown.DelayCapped {
313-
fmt.Printf(" Coordination %12s %s (capped)\n",
314-
formatCurrency(breakdown.DelayCostDetail.CoordinationCost), formatTimeUnit(breakdown.DelayCostDetail.CoordinationHours))
315-
} else {
316-
fmt.Printf(" Coordination %12s %s\n",
317-
formatCurrency(breakdown.DelayCostDetail.CoordinationCost), formatTimeUnit(breakdown.DelayCostDetail.CoordinationHours))
312+
if breakdown.DelayCostDetail.CoordinationHours > 0 {
313+
if breakdown.DelayCapped {
314+
fmt.Printf(" Coordination %12s %s (capped)\n",
315+
formatCurrency(breakdown.DelayCostDetail.CoordinationCost), formatTimeUnit(breakdown.DelayCostDetail.CoordinationHours))
316+
} else {
317+
fmt.Printf(" Coordination %12s %s\n",
318+
formatCurrency(breakdown.DelayCostDetail.CoordinationCost), formatTimeUnit(breakdown.DelayCostDetail.CoordinationHours))
319+
}
318320
}
319-
}
320321

321-
mergeDelayCost := breakdown.DelayCostDetail.DeliveryDelayCost + breakdown.DelayCostDetail.CoordinationCost
322-
mergeDelayHours := breakdown.DelayCostDetail.DeliveryDelayHours + breakdown.DelayCostDetail.CoordinationHours
323-
fmt.Println(" ────────────")
324-
fmt.Printf(" Subtotal %12s %s\n",
325-
formatCurrency(mergeDelayCost), formatTimeUnit(mergeDelayHours))
326-
fmt.Println()
322+
mergeDelayCost := breakdown.DelayCostDetail.DeliveryDelayCost + breakdown.DelayCostDetail.CoordinationCost
323+
mergeDelayHours := breakdown.DelayCostDetail.DeliveryDelayHours + breakdown.DelayCostDetail.CoordinationHours
324+
fmt.Println(" ────────────")
325+
fmt.Printf(" Subtotal %12s %s\n",
326+
formatCurrency(mergeDelayCost), formatTimeUnit(mergeDelayHours))
327+
fmt.Println()
327328

328-
// Future Costs
329-
hasFutureCosts := breakdown.DelayCostDetail.ReworkPercentage > 0 ||
330-
breakdown.DelayCostDetail.FutureReviewCost > 0 ||
331-
breakdown.DelayCostDetail.FutureMergeCost > 0 ||
332-
breakdown.DelayCostDetail.FutureContextCost > 0
333-
334-
if hasFutureCosts {
335-
fmt.Println(" Future Costs")
336-
fmt.Println(" ────────────")
337-
338-
if breakdown.DelayCostDetail.ReworkPercentage > 0 {
339-
label := fmt.Sprintf("Code Churn (%.0f%% drift)", breakdown.DelayCostDetail.ReworkPercentage)
340-
fmt.Printf(" %-24s%12s %s\n",
341-
label,
342-
formatCurrency(breakdown.DelayCostDetail.CodeChurnCost),
343-
formatTimeUnit(breakdown.DelayCostDetail.CodeChurnHours))
344-
}
329+
// Future Costs
330+
hasFutureCosts := breakdown.DelayCostDetail.ReworkPercentage > 0 ||
331+
breakdown.DelayCostDetail.FutureReviewCost > 0 ||
332+
breakdown.DelayCostDetail.FutureMergeCost > 0 ||
333+
breakdown.DelayCostDetail.FutureContextCost > 0
334+
335+
if hasFutureCosts {
336+
fmt.Println(" Future Costs")
337+
fmt.Println(" ────────────")
338+
339+
if breakdown.DelayCostDetail.ReworkPercentage > 0 {
340+
label := fmt.Sprintf("Code Churn (%.0f%% drift)", breakdown.DelayCostDetail.ReworkPercentage)
341+
fmt.Printf(" %-26s%12s %s\n",
342+
label,
343+
formatCurrency(breakdown.DelayCostDetail.CodeChurnCost),
344+
formatTimeUnit(breakdown.DelayCostDetail.CodeChurnHours))
345+
}
345346

346-
if breakdown.DelayCostDetail.FutureReviewCost > 0 {
347-
fmt.Printf(" %-24s%12s %s\n",
348-
"Review",
349-
formatCurrency(breakdown.DelayCostDetail.FutureReviewCost), formatTimeUnit(breakdown.DelayCostDetail.FutureReviewHours))
350-
}
347+
if breakdown.DelayCostDetail.FutureReviewCost > 0 {
348+
fmt.Printf(" %-26s%12s %s\n",
349+
"Review",
350+
formatCurrency(breakdown.DelayCostDetail.FutureReviewCost), formatTimeUnit(breakdown.DelayCostDetail.FutureReviewHours))
351+
}
351352

352-
if breakdown.DelayCostDetail.FutureMergeCost > 0 {
353-
fmt.Printf(" %-24s%12s %s\n",
354-
"Merge",
355-
formatCurrency(breakdown.DelayCostDetail.FutureMergeCost), formatTimeUnit(breakdown.DelayCostDetail.FutureMergeHours))
356-
}
353+
if breakdown.DelayCostDetail.FutureMergeCost > 0 {
354+
fmt.Printf(" %-26s%12s %s\n",
355+
"Merge",
356+
formatCurrency(breakdown.DelayCostDetail.FutureMergeCost), formatTimeUnit(breakdown.DelayCostDetail.FutureMergeHours))
357+
}
357358

358-
if breakdown.DelayCostDetail.FutureContextCost > 0 {
359-
fmt.Printf(" %-24s%12s %s\n",
360-
"Context Switching",
361-
formatCurrency(breakdown.DelayCostDetail.FutureContextCost), formatTimeUnit(breakdown.DelayCostDetail.FutureContextHours))
362-
}
359+
if breakdown.DelayCostDetail.FutureContextCost > 0 {
360+
fmt.Printf(" %-26s%12s %s\n",
361+
"Context Switching",
362+
formatCurrency(breakdown.DelayCostDetail.FutureContextCost), formatTimeUnit(breakdown.DelayCostDetail.FutureContextHours))
363+
}
363364

364-
futureCost := breakdown.DelayCostDetail.CodeChurnCost +
365-
breakdown.DelayCostDetail.FutureReviewCost +
366-
breakdown.DelayCostDetail.FutureMergeCost +
367-
breakdown.DelayCostDetail.FutureContextCost
368-
futureHours := breakdown.DelayCostDetail.CodeChurnHours +
369-
breakdown.DelayCostDetail.FutureReviewHours +
370-
breakdown.DelayCostDetail.FutureMergeHours +
371-
breakdown.DelayCostDetail.FutureContextHours
372-
fmt.Println(" ────────────")
373-
fmt.Printf(" Subtotal %12s %s\n",
374-
formatCurrency(futureCost), formatTimeUnit(futureHours))
375-
fmt.Println()
365+
futureCost := breakdown.DelayCostDetail.CodeChurnCost +
366+
breakdown.DelayCostDetail.FutureReviewCost +
367+
breakdown.DelayCostDetail.FutureMergeCost +
368+
breakdown.DelayCostDetail.FutureContextCost
369+
futureHours := breakdown.DelayCostDetail.CodeChurnHours +
370+
breakdown.DelayCostDetail.FutureReviewHours +
371+
breakdown.DelayCostDetail.FutureMergeHours +
372+
breakdown.DelayCostDetail.FutureContextHours
373+
fmt.Println(" ────────────")
374+
fmt.Printf(" Subtotal %12s %s\n",
375+
formatCurrency(futureCost), formatTimeUnit(futureHours))
376+
fmt.Println()
377+
}
376378
}
377379

378380
// Grand Total

cmd/prcost/repository.go

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ func formatTimeUnit(hours float64) string {
177177
// Show minutes for values less than 1 hour
178178
if hours < 1.0 {
179179
minutes := hours * 60.0
180-
return fmt.Sprintf("%.0f min", minutes)
180+
// Use 1 decimal place for better precision and clearer addition
181+
return fmt.Sprintf("%.1f min", minutes)
181182
}
182183

183184
if hours < 48 {
@@ -248,8 +249,8 @@ func printExtrapolatedResults(title string, days int, ext *cost.ExtrapolatedBrea
248249
fmt.Println()
249250

250251
// Authors section
251-
fmt.Println(" Development Cost")
252-
fmt.Println(" ────────────────")
252+
fmt.Println(" Development Costs")
253+
fmt.Println(" ────────────────")
253254
fmt.Printf(" New Development %12s %s\n",
254255
formatWithCommas(avgAuthorNewCodeCost), formatTimeUnit(avgAuthorNewCodeHours))
255256
fmt.Printf(" Adaptation %12s %s\n",
@@ -265,8 +266,8 @@ func printExtrapolatedResults(title string, days int, ext *cost.ExtrapolatedBrea
265266

266267
// Participants section (if any participants)
267268
if ext.ParticipantTotalCost > 0 {
268-
fmt.Println(" Participant Cost")
269-
fmt.Println(" ────────────────")
269+
fmt.Println(" Participant Costs")
270+
fmt.Println(" ────────────────")
270271
if avgParticipantReviewCost > 0 {
271272
fmt.Printf(" Review Activity %12s %s\n",
272273
formatWithCommas(avgParticipantReviewCost), formatTimeUnit(avgParticipantReviewHours))
@@ -312,21 +313,21 @@ func printExtrapolatedResults(title string, days int, ext *cost.ExtrapolatedBrea
312313
fmt.Println(" Future Costs")
313314
fmt.Println(" ────────────")
314315
if ext.CodeChurnCost > 0.01 {
315-
fmt.Printf(" Code Churn %12s %s\n",
316+
fmt.Printf(" Code Churn %12s %s\n",
316317
formatWithCommas(avgCodeChurnCost), formatTimeUnit(avgCodeChurnHours))
317318
}
318319
if ext.FutureReviewCost > 0.01 {
319-
fmt.Printf(" %-24s%12s %s\n",
320+
fmt.Printf(" %-26s%12s %s\n",
320321
"Review",
321322
formatWithCommas(avgFutureReviewCost), formatTimeUnit(avgFutureReviewHours))
322323
}
323324
if ext.FutureMergeCost > 0.01 {
324-
fmt.Printf(" %-24s%12s %s\n",
325+
fmt.Printf(" %-26s%12s %s\n",
325326
"Merge",
326327
formatWithCommas(avgFutureMergeCost), formatTimeUnit(avgFutureMergeHours))
327328
}
328329
if ext.FutureContextCost > 0.01 {
329-
fmt.Printf(" %-24s%12s %s\n",
330+
fmt.Printf(" %-26s%12s %s\n",
330331
"Context Switching",
331332
formatWithCommas(avgFutureContextCost), formatTimeUnit(avgFutureContextHours))
332333
}
@@ -350,8 +351,8 @@ func printExtrapolatedResults(title string, days int, ext *cost.ExtrapolatedBrea
350351
fmt.Println()
351352

352353
// Authors section (extrapolated)
353-
fmt.Println(" Development Cost")
354-
fmt.Println(" ────────────────")
354+
fmt.Println(" Development Costs")
355+
fmt.Println(" ────────────────")
355356
fmt.Printf(" New Development %12s %s\n",
356357
formatWithCommas(ext.AuthorNewCodeCost), formatTimeUnit(ext.AuthorNewCodeHours))
357358
fmt.Printf(" Adaptation %12s %s\n",
@@ -367,8 +368,8 @@ func printExtrapolatedResults(title string, days int, ext *cost.ExtrapolatedBrea
367368

368369
// Participants section (extrapolated, if any participants)
369370
if ext.ParticipantTotalCost > 0 {
370-
fmt.Println(" Participant Cost")
371-
fmt.Println(" ────────────────")
371+
fmt.Println(" Participant Costs")
372+
fmt.Println(" ────────────────")
372373
if ext.ParticipantReviewCost > 0 {
373374
fmt.Printf(" Review Activity %12s %s\n",
374375
formatWithCommas(ext.ParticipantReviewCost), formatTimeUnit(ext.ParticipantReviewHours))
@@ -407,21 +408,21 @@ func printExtrapolatedResults(title string, days int, ext *cost.ExtrapolatedBrea
407408
fmt.Println(" Future Costs")
408409
fmt.Println(" ────────────")
409410
if ext.CodeChurnCost > 0.01 {
410-
fmt.Printf(" Code Churn %12s %s\n",
411+
fmt.Printf(" Code Churn %12s %s\n",
411412
formatWithCommas(ext.CodeChurnCost), formatTimeUnit(ext.CodeChurnHours))
412413
}
413414
if ext.FutureReviewCost > 0.01 {
414-
fmt.Printf(" %-24s%12s %s\n",
415+
fmt.Printf(" %-26s%12s %s\n",
415416
"Review",
416417
formatWithCommas(ext.FutureReviewCost), formatTimeUnit(ext.FutureReviewHours))
417418
}
418419
if ext.FutureMergeCost > 0.01 {
419-
fmt.Printf(" %-24s%12s %s\n",
420+
fmt.Printf(" %-26s%12s %s\n",
420421
"Merge",
421422
formatWithCommas(ext.FutureMergeCost), formatTimeUnit(ext.FutureMergeHours))
422423
}
423424
if ext.FutureContextCost > 0.01 {
424-
fmt.Printf(" %-24s%12s %s\n",
425+
fmt.Printf(" %-26s%12s %s\n",
425426
"Context Switching",
426427
formatWithCommas(ext.FutureContextCost), formatTimeUnit(ext.FutureContextHours))
427428
}

hacks/check-config/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ func main() {
1111
cfg := cost.DefaultConfig()
1212
fmt.Printf("EventDuration: %v\n", cfg.EventDuration)
1313
fmt.Printf("SessionGapThreshold: %v\n", cfg.SessionGapThreshold)
14-
fmt.Printf("ContextSwitchDuration: %v\n", cfg.ContextSwitchDuration)
14+
fmt.Printf("ContextSwitchInDuration: %v\n", cfg.ContextSwitchInDuration)
15+
fmt.Printf("ContextSwitchOutDuration: %v\n", cfg.ContextSwitchOutDuration)
1516
}

hacks/debug-sessions/main.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,13 @@ func main() {
5151

5252
cfg := cost.DefaultConfig()
5353
gapThreshold := cfg.SessionGapThreshold
54-
contextSwitch := cfg.ContextSwitchDuration
54+
contextIn := cfg.ContextSwitchInDuration
55+
contextOut := cfg.ContextSwitchOutDuration
5556
eventDur := cfg.EventDuration
5657

5758
fmt.Printf("Gap Threshold: %v\n", gapThreshold)
58-
fmt.Printf("Context Switch: %v\n", contextSwitch)
59+
fmt.Printf("Context Switch In: %v\n", contextIn)
60+
fmt.Printf("Context Switch Out: %v\n", contextOut)
5961
fmt.Printf("Event Duration: %v\n\n", eventDur)
6062

6163
// Sort events by time
@@ -93,8 +95,8 @@ func main() {
9395
fmt.Printf("Session %d: %d events\n", sessionNum, eventsInSession)
9496

9597
// Context in
96-
totalContext += contextSwitch
97-
fmt.Printf(" Context In: %v\n", contextSwitch)
98+
totalContext += contextIn
99+
fmt.Printf(" Context In: %v\n", contextIn)
98100

99101
// First event
100102
totalGitHub += eventDur
@@ -112,11 +114,11 @@ func main() {
112114
}
113115

114116
// Context out
115-
totalContext += contextSwitch
116-
fmt.Printf(" Context Out: %v\n", contextSwitch)
117+
totalContext += contextOut
118+
fmt.Printf(" Context Out: %v\n", contextOut)
117119
fmt.Printf(" Session Total - GitHub: %v, Context: %v\n\n",
118120
(time.Duration(eventsInSession) * eventDur),
119-
2*contextSwitch)
121+
contextIn+contextOut)
120122

121123
i = end + 1
122124
}

0 commit comments

Comments
 (0)