@@ -205,6 +205,38 @@ func formatTimeUnit(hours float64) string {
205205 return fmt .Sprintf ("%.1f years" , years )
206206}
207207
208+ // formatEngTimeUnit formats time for annual metrics.
209+ // Uses standard time units (months, years) for large durations.
210+ func formatEngTimeUnit (hours float64 ) string {
211+ // Show minutes for values less than 1 hour
212+ if hours < 1.0 {
213+ minutes := hours * 60.0
214+ return fmt .Sprintf ("%.1f min" , minutes )
215+ }
216+
217+ if hours < 48 {
218+ return fmt .Sprintf ("%.1f hrs" , hours )
219+ }
220+
221+ days := hours / 24.0
222+ if days < 14 {
223+ return fmt .Sprintf ("%.1f days" , days )
224+ }
225+
226+ weeks := days / 7.0
227+ if weeks < 8 {
228+ return fmt .Sprintf ("%.1f weeks" , weeks )
229+ }
230+
231+ months := days / 30.0
232+ if months < 24 {
233+ return fmt .Sprintf ("%.1f months" , months )
234+ }
235+
236+ years := days / 365.0
237+ return fmt .Sprintf ("%.1f years" , years )
238+ }
239+
208240// printExtrapolatedResults displays extrapolated cost breakdown in itemized format.
209241//
210242//nolint:maintidx,revive // acceptable complexity/length for comprehensive display function
@@ -264,7 +296,7 @@ func printExtrapolatedResults(title string, days int, ext *cost.ExtrapolatedBrea
264296 formatWithCommas (avgAuthorGitHubContextCost ), formatTimeUnit (avgAuthorGitHubContextHours ))
265297 fmt .Println (" ────────────" )
266298 pct := (avgAuthorTotalCost / avgTotalCost ) * 100
267- fmt .Printf (" Subtotal %12s %s (%.1f%%)\n " ,
299+ fmt .Printf (" Subtotal $ %12s %s (%.1f%%)\n " ,
268300 formatWithCommas (avgAuthorTotalCost ), formatTimeUnit (avgAuthorTotalHours ), pct )
269301 fmt .Println ()
270302
@@ -277,30 +309,30 @@ func printExtrapolatedResults(title string, days int, ext *cost.ExtrapolatedBrea
277309 formatWithCommas (avgParticipantReviewCost ), formatTimeUnit (avgParticipantReviewHours ))
278310 }
279311 if avgParticipantGitHubCost > 0 {
280- fmt .Printf (" GitHub Events %12s %s\n " ,
312+ fmt .Printf (" GitHub Activity %12s %s\n " ,
281313 formatWithCommas (avgParticipantGitHubCost ), formatTimeUnit (avgParticipantGitHubHours ))
282314 }
283315 fmt .Printf (" Context Switching %12s %s\n " ,
284316 formatWithCommas (avgParticipantContextCost ), formatTimeUnit (avgParticipantContextHours ))
285317 fmt .Println (" ────────────" )
286318 pct := (avgParticipantTotalCost / avgTotalCost ) * 100
287- fmt .Printf (" Subtotal %12s %s (%.1f%%)\n " ,
319+ fmt .Printf (" Subtotal $ %12s %s (%.1f%%)\n " ,
288320 formatWithCommas (avgParticipantTotalCost ), formatTimeUnit (avgParticipantTotalHours ), pct )
289321 fmt .Println ()
290322 }
291323
292324 // Merge Delay section
293325 fmt .Println (" Delay Costs" )
294326 fmt .Println (" ───────────" )
295- fmt .Printf (" Project %12s %s\n " ,
327+ fmt .Printf (" Workstream blockage %12s %s\n " ,
296328 formatWithCommas (avgDeliveryDelayCost ), formatTimeUnit (avgDeliveryDelayHours ))
297329 fmt .Printf (" Coordination %12s %s\n " ,
298330 formatWithCommas (avgCoordinationCost ), formatTimeUnit (avgCoordinationHours ))
299331 avgMergeDelayCost := avgDeliveryDelayCost + avgCoordinationCost
300332 avgMergeDelayHours := avgDeliveryDelayHours + avgCoordinationHours
301333 fmt .Println (" ────────────" )
302334 pct = (avgMergeDelayCost / avgTotalCost ) * 100
303- fmt .Printf (" Subtotal %12s %s (%.1f%%)\n " ,
335+ fmt .Printf (" Subtotal $ %12s %s (%.1f%%)\n " ,
304336 formatWithCommas (avgMergeDelayCost ), formatTimeUnit (avgMergeDelayHours ), pct )
305337 fmt .Println ()
306338
@@ -341,22 +373,23 @@ func printExtrapolatedResults(title string, days int, ext *cost.ExtrapolatedBrea
341373 avgFutureHours := avgCodeChurnHours + avgFutureReviewHours + avgFutureMergeHours + avgFutureContextHours
342374 fmt .Println (" ────────────" )
343375 pct = (avgFutureCost / avgTotalCost ) * 100
344- fmt .Printf (" Subtotal %12s %s (%.1f%%)\n " ,
376+ fmt .Printf (" Subtotal $ %12s %s (%.1f%%)\n " ,
345377 formatWithCommas (avgFutureCost ), formatTimeUnit (avgFutureHours ), pct )
346378 fmt .Println ()
347379 }
348380
349381 // Average total
350382 fmt .Println (" ═══════════════════════════════════════════════════════════════" )
351- fmt .Printf (" Average Total %12s %s\n " ,
383+ fmt .Printf (" Average Total $ %12s %s\n " ,
352384 formatWithCommas (avgTotalCost ), formatTimeUnit (avgTotalHours ))
353385 fmt .Println ()
354386 fmt .Println ()
355387
356388 // Extrapolated total section with improved visual hierarchy
357389 fmt .Println (" ┌─────────────────────────────────────────────────────────────┐" )
358- fmt .Printf (" │ Estimated Total Changes (extrapolated across %d day period)%*s│\n " , days , 35 - len (strconv .Itoa (days )), "" )
359- fmt .Printf (" │ Total PRs: %d%*s│\n " , ext .TotalPRs , 60 - len (strconv .Itoa (ext .TotalPRs )), "" )
390+ headerText := fmt .Sprintf ("Estimated costs within a %d day period (extrapolated)" , days )
391+ padding := 61 - len (headerText )
392+ fmt .Printf (" │ %s%*s│\n " , headerText , padding , "" )
360393 fmt .Println (" └─────────────────────────────────────────────────────────────┘" )
361394 fmt .Println ()
362395
@@ -373,7 +406,7 @@ func printExtrapolatedResults(title string, days int, ext *cost.ExtrapolatedBrea
373406 formatWithCommas (ext .AuthorGitHubContextCost ), formatTimeUnit (ext .AuthorGitHubContextHours ))
374407 fmt .Println (" ────────────" )
375408 pct = (ext .AuthorTotalCost / ext .TotalCost ) * 100
376- fmt .Printf (" Subtotal %12s %s (%.1f%%)\n " ,
409+ fmt .Printf (" Subtotal $ %12s %s (%.1f%%)\n " ,
377410 formatWithCommas (ext .AuthorTotalCost ), formatTimeUnit (ext .AuthorTotalHours ), pct )
378411 fmt .Println ()
379412
@@ -386,30 +419,30 @@ func printExtrapolatedResults(title string, days int, ext *cost.ExtrapolatedBrea
386419 formatWithCommas (ext .ParticipantReviewCost ), formatTimeUnit (ext .ParticipantReviewHours ))
387420 }
388421 if ext .ParticipantGitHubCost > 0 {
389- fmt .Printf (" GitHub Events %12s %s\n " ,
422+ fmt .Printf (" GitHub Activity %12s %s\n " ,
390423 formatWithCommas (ext .ParticipantGitHubCost ), formatTimeUnit (ext .ParticipantGitHubHours ))
391424 }
392425 fmt .Printf (" Context Switching %12s %s\n " ,
393426 formatWithCommas (ext .ParticipantContextCost ), formatTimeUnit (ext .ParticipantContextHours ))
394427 fmt .Println (" ────────────" )
395428 pct = (ext .ParticipantTotalCost / ext .TotalCost ) * 100
396- fmt .Printf (" Subtotal %12s %s (%.1f%%)\n " ,
429+ fmt .Printf (" Subtotal $ %12s %s (%.1f%%)\n " ,
397430 formatWithCommas (ext .ParticipantTotalCost ), formatTimeUnit (ext .ParticipantTotalHours ), pct )
398431 fmt .Println ()
399432 }
400433
401434 // Merge Delay section (extrapolated)
402435 fmt .Println (" Delay Costs" )
403436 fmt .Println (" ───────────" )
404- fmt .Printf (" Project %12s %s\n " ,
437+ fmt .Printf (" Workstream blockage %12s %s\n " ,
405438 formatWithCommas (ext .DeliveryDelayCost ), formatTimeUnit (ext .DeliveryDelayHours ))
406439 fmt .Printf (" Coordination %12s %s\n " ,
407440 formatWithCommas (ext .CoordinationCost ), formatTimeUnit (ext .CoordinationHours ))
408441 extMergeDelayCost := ext .DeliveryDelayCost + ext .CoordinationCost
409442 extMergeDelayHours := ext .DeliveryDelayHours + ext .CoordinationHours
410443 fmt .Println (" ────────────" )
411444 pct = (extMergeDelayCost / ext .TotalCost ) * 100
412- fmt .Printf (" Subtotal %12s %s (%.1f%%)\n " ,
445+ fmt .Printf (" Subtotal $ %12s %s (%.1f%%)\n " ,
413446 formatWithCommas (extMergeDelayCost ), formatTimeUnit (extMergeDelayHours ), pct )
414447 fmt .Println ()
415448
@@ -443,14 +476,55 @@ func printExtrapolatedResults(title string, days int, ext *cost.ExtrapolatedBrea
443476 extFutureHours := ext .CodeChurnHours + ext .FutureReviewHours + ext .FutureMergeHours + ext .FutureContextHours
444477 fmt .Println (" ────────────" )
445478 pct = (extFutureCost / ext .TotalCost ) * 100
446- fmt .Printf (" Subtotal %12s %s (%.1f%%)\n " ,
479+ fmt .Printf (" Subtotal $ %12s %s (%.1f%%)\n " ,
447480 formatWithCommas (extFutureCost ), formatTimeUnit (extFutureHours ), pct )
448481 fmt .Println ()
449482 }
450483
451484 // Extrapolated grand total
452485 fmt .Println (" ═══════════════════════════════════════════════════════════════" )
453- fmt .Printf (" Total %12s %s\n " ,
486+ fmt .Printf (" Total $ %12s %s\n " ,
454487 formatWithCommas (ext .TotalCost ), formatTimeUnit (ext .TotalHours ))
455488 fmt .Println ()
489+
490+ // Print extrapolated efficiency score + annual waste
491+ printExtrapolatedEfficiency (ext , days )
492+ }
493+
494+ // printExtrapolatedEfficiency prints the workflow efficiency + annual waste section for extrapolated totals.
495+ func printExtrapolatedEfficiency (ext * cost.ExtrapolatedBreakdown , days int ) {
496+ // Calculate preventable waste: Code Churn + All Delay Costs
497+ preventableHours := ext .CodeChurnHours + ext .DeliveryDelayHours + ext .CoordinationHours
498+ preventableCost := ext .CodeChurnCost + ext .DeliveryDelayCost + ext .CoordinationCost
499+
500+ // Calculate efficiency
501+ var efficiencyPct float64
502+ if ext .TotalHours > 0 {
503+ efficiencyPct = 100.0 * (ext .TotalHours - preventableHours ) / ext .TotalHours
504+ } else {
505+ efficiencyPct = 100.0
506+ }
507+
508+ grade , message := efficiencyGrade (efficiencyPct )
509+
510+ // Calculate annual waste
511+ annualMultiplier := 365.0 / float64 (days )
512+ annualWasteHours := preventableHours * annualMultiplier
513+ annualWasteCost := preventableCost * annualMultiplier
514+
515+ fmt .Println (" ┌─────────────────────────────────────────────────────────────┐" )
516+ fmt .Printf (" │ WORKFLOW EFFICIENCY: %s (%.1f%%) - %s" , grade , efficiencyPct , message )
517+ // Calculate padding to reach 63 chars (box width)
518+ headerLen := 25 + len (grade ) + len (fmt .Sprintf ("%.1f" , efficiencyPct )) + len (message )
519+ padding := 63 - headerLen
520+ if padding < 0 {
521+ padding = 0
522+ }
523+ fmt .Printf ("%*s│\n " , padding , "" )
524+ fmt .Println (" └─────────────────────────────────────────────────────────────┘" )
525+ fmt .Printf (" Preventable Waste: $%12s %s\n " ,
526+ formatWithCommas (preventableCost ), formatTimeUnit (preventableHours ))
527+ fmt .Printf (" Annual Wasted Effort: $%12s %s\n " ,
528+ formatWithCommas (annualWasteCost ), formatEngTimeUnit (annualWasteHours ))
529+ fmt .Println ()
456530}
0 commit comments