Skip to content

Commit 5d06155

Browse files
Thomas StrombergThomas Stromberg
authored andcommitted
Add missing counters
1 parent 9f0d6b9 commit 5d06155

File tree

8 files changed

+190
-176
lines changed

8 files changed

+190
-176
lines changed

cmd/prcost/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,13 @@ func formatWithCommas(amount float64) string {
428428

429429
// formatLOC formats lines of code in kilo format with appropriate precision and commas for large values.
430430
func formatLOC(kloc float64) string {
431+
loc := kloc * 1000 // Convert to actual lines
432+
433+
// For values < 1k LOC, just show LOC count without 'k' suffix
434+
if loc < 1000 {
435+
return fmt.Sprintf("%d LOC", int(loc))
436+
}
437+
431438
// For values >= 100k, add commas (e.g., "1,517k" instead of "1517k")
432439
if kloc >= 100.0 {
433440
intPart := int(kloc)

cmd/prcost/repository.go

Lines changed: 40 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ func isBotAuthor(author string) bool {
6060
// Uses library functions from pkg/github and pkg/cost for fetching, sampling,
6161
// and extrapolation - all functionality is available to external clients.
6262
func analyzeRepository(ctx context.Context, owner, repo string, sampleSize, days int, cfg cost.Config, token string, dataSource string) error {
63-
6463
// Calculate since date
6564
since := time.Now().AddDate(0, 0, -days)
6665

@@ -283,28 +282,13 @@ func analyzeOrganization(ctx context.Context, org string, sampleSize, days int,
283282
// Count unique authors across all PRs (not just samples)
284283
totalAuthors := github.CountUniqueAuthors(prs)
285284

286-
// Count open PRs across all unique repos in the organization
287-
uniqueRepos := make(map[string]bool)
288-
for _, pr := range prs {
289-
repoKey := pr.Owner + "/" + pr.Repo
290-
uniqueRepos[repoKey] = true
291-
}
292-
293-
totalOpenPRs := 0
294-
for repoKey := range uniqueRepos {
295-
parts := strings.SplitN(repoKey, "/", 2)
296-
if len(parts) != 2 {
297-
continue
298-
}
299-
owner, repo := parts[0], parts[1]
300-
openCount, err := github.CountOpenPRsInRepo(ctx, owner, repo, token)
301-
if err != nil {
302-
slog.Warn("Failed to count open PRs for repo", "repo", repoKey, "error", err)
303-
continue
304-
}
305-
totalOpenPRs += openCount
285+
// Count open PRs across the entire organization with a single query
286+
totalOpenPRs, err := github.CountOpenPRsInOrg(ctx, org, token)
287+
if err != nil {
288+
slog.Warn("Failed to count open PRs in organization, using 0", "error", err)
289+
totalOpenPRs = 0
306290
}
307-
slog.Info("Counted total open PRs across organization", "open_prs", totalOpenPRs, "repos", len(uniqueRepos))
291+
slog.Info("Counted total open PRs across organization", "org", org, "open_prs", totalOpenPRs)
308292

309293
// Extrapolate costs from samples using library function
310294
extrapolated := cost.ExtrapolateFromSamples(breakdowns, len(prs), totalAuthors, totalOpenPRs, actualDays, cfg)
@@ -433,14 +417,18 @@ func printExtrapolatedResults(title string, days int, ext *cost.ExtrapolatedBrea
433417
fmt.Printf(" Development Costs (%d PRs, %s total LOC)\n", ext.HumanPRs, totalLOCStr)
434418
fmt.Println(" ────────────────────────────────────────")
435419

420+
// Calculate average events and sessions
421+
avgAuthorEvents := float64(ext.AuthorEvents) / float64(ext.TotalPRs)
422+
avgAuthorSessions := float64(ext.AuthorSessions) / float64(ext.TotalPRs)
423+
436424
fmt.Printf(" New Development $%10s %s (%s LOC)\n",
437425
formatWithCommas(avgAuthorNewCodeCost), formatTimeUnit(avgAuthorNewCodeHours), newLOCStr)
438426
fmt.Printf(" Adaptation $%10s %s (%s LOC)\n",
439427
formatWithCommas(avgAuthorAdaptationCost), formatTimeUnit(avgAuthorAdaptationHours), modifiedLOCStr)
440-
fmt.Printf(" GitHub Activity $%10s %s\n",
441-
formatWithCommas(avgAuthorGitHubCost), formatTimeUnit(avgAuthorGitHubHours))
442-
fmt.Printf(" Context Switching $%10s %s\n",
443-
formatWithCommas(avgAuthorGitHubContextCost), formatTimeUnit(avgAuthorGitHubContextHours))
428+
fmt.Printf(" GitHub Activity $%10s %s (%.1f events)\n",
429+
formatWithCommas(avgAuthorGitHubCost), formatTimeUnit(avgAuthorGitHubHours), avgAuthorEvents)
430+
fmt.Printf(" Context Switching $%10s %s (%.1f sessions)\n",
431+
formatWithCommas(avgAuthorGitHubContextCost), formatTimeUnit(avgAuthorGitHubContextHours), avgAuthorSessions)
444432

445433
// Show bot PR LOC even though cost is $0
446434
if ext.BotPRs > 0 {
@@ -457,18 +445,23 @@ func printExtrapolatedResults(title string, days int, ext *cost.ExtrapolatedBrea
457445

458446
// Participants section (if any participants)
459447
if ext.ParticipantTotalCost > 0 {
448+
avgParticipantEvents := float64(ext.ParticipantEvents) / float64(ext.TotalPRs)
449+
avgParticipantSessions := float64(ext.ParticipantSessions) / float64(ext.TotalPRs)
450+
451+
avgParticipantReviews := float64(ext.ParticipantReviews) / float64(ext.TotalPRs)
452+
460453
fmt.Println(" Participant Costs")
461454
fmt.Println(" ─────────────────")
462455
if avgParticipantReviewCost > 0 {
463-
fmt.Printf(" Review Activity $%10s %s\n",
464-
formatWithCommas(avgParticipantReviewCost), formatTimeUnit(avgParticipantReviewHours))
456+
fmt.Printf(" Review Activity $%10s %s (%.1f reviews)\n",
457+
formatWithCommas(avgParticipantReviewCost), formatTimeUnit(avgParticipantReviewHours), avgParticipantReviews)
465458
}
466459
if avgParticipantGitHubCost > 0 {
467-
fmt.Printf(" GitHub Activity $%10s %s\n",
468-
formatWithCommas(avgParticipantGitHubCost), formatTimeUnit(avgParticipantGitHubHours))
460+
fmt.Printf(" GitHub Activity $%10s %s (%.1f events)\n",
461+
formatWithCommas(avgParticipantGitHubCost), formatTimeUnit(avgParticipantGitHubHours), avgParticipantEvents)
469462
}
470-
fmt.Printf(" Context Switching $%10s %s\n",
471-
formatWithCommas(avgParticipantContextCost), formatTimeUnit(avgParticipantContextHours))
463+
fmt.Printf(" Context Switching $%10s %s (%.1f sessions)\n",
464+
formatWithCommas(avgParticipantContextCost), formatTimeUnit(avgParticipantContextHours), avgParticipantSessions)
472465
fmt.Println(" ──────────")
473466
participantPct := (avgParticipantTotalCost / avgTotalCost) * 100
474467
fmt.Printf(" Subtotal $%10s %s (%.1f%%)\n",
@@ -533,8 +526,9 @@ func printExtrapolatedResults(title string, days int, ext *cost.ExtrapolatedBrea
533526
formatWithCommas(avgFutureMergeCost), formatTimeUnit(avgFutureMergeHours), ext.FutureMergePRCount)
534527
}
535528
if ext.FutureContextCost > 0.01 {
536-
fmt.Printf(" Context Switching $%10s %s\n",
537-
formatWithCommas(avgFutureContextCost), formatTimeUnit(avgFutureContextHours))
529+
avgFutureContextSessions := float64(ext.FutureContextSessions) / float64(ext.TotalPRs)
530+
fmt.Printf(" Context Switching $%10s %s (%.1f sessions)\n",
531+
formatWithCommas(avgFutureContextCost), formatTimeUnit(avgFutureContextHours), avgFutureContextSessions)
538532
}
539533
avgFutureCost := avgCodeChurnCost + avgFutureReviewCost + avgFutureMergeCost + avgFutureContextCost
540534
avgFutureHours := avgCodeChurnHours + avgFutureReviewHours + avgFutureMergeHours + avgFutureContextHours
@@ -589,10 +583,10 @@ func printExtrapolatedResults(title string, days int, ext *cost.ExtrapolatedBrea
589583
formatWithCommas(ext.AuthorNewCodeCost), formatTimeUnit(ext.AuthorNewCodeHours), totalNewLOCStr)
590584
fmt.Printf(" Adaptation $%10s %s (%s LOC)\n",
591585
formatWithCommas(ext.AuthorAdaptationCost), formatTimeUnit(ext.AuthorAdaptationHours), totalModifiedLOCStr)
592-
fmt.Printf(" GitHub Activity $%10s %s\n",
593-
formatWithCommas(ext.AuthorGitHubCost), formatTimeUnit(ext.AuthorGitHubHours))
594-
fmt.Printf(" Context Switching $%10s %s\n",
595-
formatWithCommas(ext.AuthorGitHubContextCost), formatTimeUnit(ext.AuthorGitHubContextHours))
586+
fmt.Printf(" GitHub Activity $%10s %s (%d events)\n",
587+
formatWithCommas(ext.AuthorGitHubCost), formatTimeUnit(ext.AuthorGitHubHours), ext.AuthorEvents)
588+
fmt.Printf(" Context Switching $%10s %s (%d sessions)\n",
589+
formatWithCommas(ext.AuthorGitHubContextCost), formatTimeUnit(ext.AuthorGitHubContextHours), ext.AuthorSessions)
596590

597591
// Show bot PR LOC even though cost is $0
598592
if ext.BotPRs > 0 {
@@ -612,15 +606,15 @@ func printExtrapolatedResults(title string, days int, ext *cost.ExtrapolatedBrea
612606
fmt.Println(" Participant Costs")
613607
fmt.Println(" ─────────────────")
614608
if ext.ParticipantReviewCost > 0 {
615-
fmt.Printf(" Review Activity $%10s %s\n",
616-
formatWithCommas(ext.ParticipantReviewCost), formatTimeUnit(ext.ParticipantReviewHours))
609+
fmt.Printf(" Review Activity $%10s %s (%d reviews)\n",
610+
formatWithCommas(ext.ParticipantReviewCost), formatTimeUnit(ext.ParticipantReviewHours), ext.ParticipantReviews)
617611
}
618612
if ext.ParticipantGitHubCost > 0 {
619-
fmt.Printf(" GitHub Activity $%10s %s\n",
620-
formatWithCommas(ext.ParticipantGitHubCost), formatTimeUnit(ext.ParticipantGitHubHours))
613+
fmt.Printf(" GitHub Activity $%10s %s (%d events)\n",
614+
formatWithCommas(ext.ParticipantGitHubCost), formatTimeUnit(ext.ParticipantGitHubHours), ext.ParticipantEvents)
621615
}
622-
fmt.Printf(" Context Switching $%10s %s\n",
623-
formatWithCommas(ext.ParticipantContextCost), formatTimeUnit(ext.ParticipantContextHours))
616+
fmt.Printf(" Context Switching $%10s %s (%d sessions)\n",
617+
formatWithCommas(ext.ParticipantContextCost), formatTimeUnit(ext.ParticipantContextHours), ext.ParticipantSessions)
624618
fmt.Println(" ──────────")
625619
pct = (ext.ParticipantTotalCost / ext.TotalCost) * 100
626620
fmt.Printf(" Subtotal $%10s %s (%.1f%%)\n",
@@ -681,8 +675,8 @@ func printExtrapolatedResults(title string, days int, ext *cost.ExtrapolatedBrea
681675
formatWithCommas(ext.FutureMergeCost), formatTimeUnit(ext.FutureMergeHours), ext.FutureMergePRCount)
682676
}
683677
if ext.FutureContextCost > 0.01 {
684-
fmt.Printf(" Context Switching $%10s %s\n",
685-
formatWithCommas(ext.FutureContextCost), formatTimeUnit(ext.FutureContextHours))
678+
fmt.Printf(" Context Switching $%10s %s (%d sessions)\n",
679+
formatWithCommas(ext.FutureContextCost), formatTimeUnit(ext.FutureContextHours), ext.FutureContextSessions)
686680
}
687681
extFutureCost := ext.CodeChurnCost + ext.FutureReviewCost + ext.FutureMergeCost + ext.FutureContextCost
688682
extFutureHours := ext.CodeChurnHours + ext.FutureReviewHours + ext.FutureMergeHours + ext.FutureContextHours

internal/server/server.go

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,28 +1412,13 @@ func (s *Server) processOrgSample(ctx context.Context, req *OrgSampleRequest, to
14121412
// Count unique authors across all PRs (not just samples)
14131413
totalAuthors := github.CountUniqueAuthors(prs)
14141414

1415-
// Count open PRs across all unique repos in the organization
1416-
uniqueRepos := make(map[string]bool)
1417-
for _, pr := range prs {
1418-
repoKey := pr.Owner + "/" + pr.Repo
1419-
uniqueRepos[repoKey] = true
1420-
}
1421-
1422-
totalOpenPRs := 0
1423-
for repoKey := range uniqueRepos {
1424-
parts := strings.SplitN(repoKey, "/", 2)
1425-
if len(parts) != 2 {
1426-
continue
1427-
}
1428-
owner, repo := parts[0], parts[1]
1429-
openCount, err := github.CountOpenPRsInRepo(ctx, owner, repo, token)
1430-
if err != nil {
1431-
s.logger.WarnContext(ctx, "Failed to count open PRs for repo", "repo", repoKey, errorKey, err)
1432-
continue
1433-
}
1434-
totalOpenPRs += openCount
1415+
// Count open PRs across the entire organization with a single query
1416+
totalOpenPRs, err := github.CountOpenPRsInOrg(ctx, req.Org, token)
1417+
if err != nil {
1418+
s.logger.WarnContext(ctx, "Failed to count open PRs in organization, using 0", errorKey, err)
1419+
totalOpenPRs = 0
14351420
}
1436-
s.logger.InfoContext(ctx, "Counted total open PRs across organization", "open_prs", totalOpenPRs, "repos", len(uniqueRepos))
1421+
s.logger.InfoContext(ctx, "Counted total open PRs across organization", "org", req.Org, "open_prs", totalOpenPRs)
14371422

14381423
// Extrapolate costs from samples
14391424
extrapolated := cost.ExtrapolateFromSamples(breakdowns, len(prs), totalAuthors, totalOpenPRs, actualDays, cfg)

0 commit comments

Comments
 (0)