mirror of
https://github.com/lukaszraczylo/git-velocity.git
synced 2026-06-13 02:51:52 +00:00
Add additional sections.
This commit is contained in:
@@ -139,6 +139,8 @@ func (a *Aggregator) Aggregate(data *models.RawData, dateRange *config.ParsedDat
|
||||
cm.LinesDeleted += commit.Deletions
|
||||
cm.MeaningfulLinesAdded += commit.MeaningfulAdditions
|
||||
cm.MeaningfulLinesDeleted += commit.MeaningfulDeletions
|
||||
cm.CommentLinesAdded += commit.CommentAdditions
|
||||
cm.CommentLinesDeleted += commit.CommentDeletions
|
||||
cm.FilesChanged += commit.FilesChanged
|
||||
|
||||
// Update per-repo contributor stats
|
||||
@@ -148,6 +150,8 @@ func (a *Aggregator) Aggregate(data *models.RawData, dateRange *config.ParsedDat
|
||||
rcm.LinesDeleted += commit.Deletions
|
||||
rcm.MeaningfulLinesAdded += commit.MeaningfulAdditions
|
||||
rcm.MeaningfulLinesDeleted += commit.MeaningfulDeletions
|
||||
rcm.CommentLinesAdded += commit.CommentAdditions
|
||||
rcm.CommentLinesDeleted += commit.CommentDeletions
|
||||
rcm.FilesChanged += commit.FilesChanged
|
||||
|
||||
// Track activity patterns based on commit time
|
||||
|
||||
@@ -357,5 +357,19 @@ func defaultAchievements() []AchievementConfig {
|
||||
{ID: "ooh-25", Name: "Flexible Schedule", Description: "25 commits outside 9am-5pm", Icon: "fa-user-clock", Condition: AchievementCondition{Type: "out_of_hours_count", Threshold: 25}},
|
||||
{ID: "ooh-50", Name: "Off-Hours Hero", Description: "50 commits outside 9am-5pm", Icon: "fa-hourglass-half", Condition: AchievementCondition{Type: "out_of_hours_count", Threshold: 50}},
|
||||
{ID: "ooh-100", Name: "Time Bender", Description: "100 commits outside 9am-5pm", Icon: "fa-infinity", Condition: AchievementCondition{Type: "out_of_hours_count", Threshold: 100}},
|
||||
|
||||
// ===== DOCUMENTATION & COMMENTS ADDED (Tiers: 100, 500, 1000, 2500, 5000) =====
|
||||
{ID: "docs-100", Name: "Documenter", Description: "Added 100 lines of comments/docs", Icon: "fa-file-lines", Condition: AchievementCondition{Type: "comment_lines_added", Threshold: 100}},
|
||||
{ID: "docs-500", Name: "Technical Writer", Description: "Added 500 lines of comments/docs", Icon: "fa-book", Condition: AchievementCondition{Type: "comment_lines_added", Threshold: 500}},
|
||||
{ID: "docs-1000", Name: "Documentation Hero", Description: "Added 1000 lines of comments/docs", Icon: "fa-book-open", Condition: AchievementCondition{Type: "comment_lines_added", Threshold: 1000}},
|
||||
{ID: "docs-2500", Name: "Knowledge Keeper", Description: "Added 2500 lines of comments/docs", Icon: "fa-scroll", Condition: AchievementCondition{Type: "comment_lines_added", Threshold: 2500}},
|
||||
{ID: "docs-5000", Name: "Code Historian", Description: "Added 5000 lines of comments/docs", Icon: "fa-landmark", Condition: AchievementCondition{Type: "comment_lines_added", Threshold: 5000}},
|
||||
|
||||
// ===== COMMENT CLEANUP (Tiers: 50, 200, 500, 1000, 2500) =====
|
||||
{ID: "docs-del-50", Name: "Comment Trimmer", Description: "Removed 50 lines of outdated comments", Icon: "fa-scissors", Condition: AchievementCondition{Type: "comment_lines_deleted", Threshold: 50}},
|
||||
{ID: "docs-del-200", Name: "Cleanup Crew", Description: "Removed 200 lines of outdated comments", Icon: "fa-broom", Condition: AchievementCondition{Type: "comment_lines_deleted", Threshold: 200}},
|
||||
{ID: "docs-del-500", Name: "Dead Code Hunter", Description: "Removed 500 lines of outdated comments", Icon: "fa-skull-crossbones", Condition: AchievementCondition{Type: "comment_lines_deleted", Threshold: 500}},
|
||||
{ID: "docs-del-1000", Name: "Comment Surgeon", Description: "Removed 1000 lines of outdated comments", Icon: "fa-scalpel", Condition: AchievementCondition{Type: "comment_lines_deleted", Threshold: 1000}},
|
||||
{ID: "docs-del-2500", Name: "Noise Eliminator", Description: "Removed 2500 lines of outdated comments", Icon: "fa-volume-xmark", Condition: AchievementCondition{Type: "comment_lines_deleted", Threshold: 2500}},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@ type Commit struct {
|
||||
MeaningfulAdditions int `json:"meaningful_additions"`
|
||||
MeaningfulDeletions int `json:"meaningful_deletions"`
|
||||
|
||||
// Comment line counts
|
||||
CommentAdditions int `json:"comment_additions"`
|
||||
CommentDeletions int `json:"comment_deletions"`
|
||||
|
||||
// Derived fields
|
||||
HasTests bool `json:"has_tests"`
|
||||
}
|
||||
|
||||
@@ -27,6 +27,10 @@ type ContributorMetrics struct {
|
||||
MeaningfulLinesAdded int `json:"meaningful_lines_added"`
|
||||
MeaningfulLinesDeleted int `json:"meaningful_lines_deleted"`
|
||||
|
||||
// Comment and documentation line counts
|
||||
CommentLinesAdded int `json:"comment_lines_added"`
|
||||
CommentLinesDeleted int `json:"comment_lines_deleted"`
|
||||
|
||||
// PR metrics
|
||||
PRsOpened int `json:"prs_opened"`
|
||||
PRsMerged int `json:"prs_merged"`
|
||||
|
||||
@@ -42,6 +42,8 @@ func (c *Calculator) Calculate(metrics *models.GlobalMetrics) *models.GlobalMetr
|
||||
existing.LinesDeleted += cm.LinesDeleted
|
||||
existing.MeaningfulLinesAdded += cm.MeaningfulLinesAdded
|
||||
existing.MeaningfulLinesDeleted += cm.MeaningfulLinesDeleted
|
||||
existing.CommentLinesAdded += cm.CommentLinesAdded
|
||||
existing.CommentLinesDeleted += cm.CommentLinesDeleted
|
||||
existing.PRsOpened += cm.PRsOpened
|
||||
existing.PRsMerged += cm.PRsMerged
|
||||
existing.ReviewsGiven += cm.ReviewsGiven
|
||||
@@ -257,6 +259,11 @@ func (c *Calculator) checkAchievements(cm *models.ContributorMetrics) []string {
|
||||
earned = float64(cm.OutOfHoursCount) >= ach.Condition.Threshold
|
||||
case "work_week_streak":
|
||||
earned = float64(cm.WorkWeekStreak) >= ach.Condition.Threshold
|
||||
// Documentation & comments
|
||||
case "comment_lines_added":
|
||||
earned = float64(cm.CommentLinesAdded) >= ach.Condition.Threshold
|
||||
case "comment_lines_deleted":
|
||||
earned = float64(cm.CommentLinesDeleted) >= ach.Condition.Threshold
|
||||
}
|
||||
|
||||
if earned {
|
||||
|
||||
@@ -878,3 +878,128 @@ func TestCalculator_MeaningfulLinesScoring(t *testing.T) {
|
||||
assert.Equal(t, 50, contributor.Score.Total)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCalculator_CommentLinesAchievements(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("earns documentation achievements for adding comments", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Scoring.Enabled = true
|
||||
calc := NewCalculator(cfg)
|
||||
|
||||
metrics := &models.GlobalMetrics{
|
||||
Repositories: []models.RepositoryMetrics{
|
||||
{
|
||||
FullName: "owner/repo",
|
||||
Contributors: []models.ContributorMetrics{
|
||||
{
|
||||
Login: "documenter",
|
||||
CommitCount: 10,
|
||||
CommentLinesAdded: 1500, // Should earn docs-100, docs-500, docs-1000
|
||||
CommentLinesDeleted: 100, // Should earn docs-del-50
|
||||
RepositoriesContributed: []string{"owner/repo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
result := calc.Calculate(metrics)
|
||||
|
||||
contributor := result.Repositories[0].Contributors[0]
|
||||
// Should have documentation achievements
|
||||
assert.Contains(t, contributor.Achievements, "docs-100", "Should earn docs-100 for 100+ comment lines")
|
||||
assert.Contains(t, contributor.Achievements, "docs-500", "Should earn docs-500 for 500+ comment lines")
|
||||
assert.Contains(t, contributor.Achievements, "docs-1000", "Should earn docs-1000 for 1000+ comment lines")
|
||||
assert.NotContains(t, contributor.Achievements, "docs-2500", "Should not earn docs-2500 for <2500 comment lines")
|
||||
// Should have comment cleanup achievement
|
||||
assert.Contains(t, contributor.Achievements, "docs-del-50", "Should earn docs-del-50 for 50+ comment deletions")
|
||||
assert.NotContains(t, contributor.Achievements, "docs-del-200", "Should not earn docs-del-200 for <200 deletions")
|
||||
})
|
||||
|
||||
t.Run("earns all documentation deletion achievements", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Scoring.Enabled = true
|
||||
calc := NewCalculator(cfg)
|
||||
|
||||
metrics := &models.GlobalMetrics{
|
||||
Repositories: []models.RepositoryMetrics{
|
||||
{
|
||||
FullName: "owner/repo",
|
||||
Contributors: []models.ContributorMetrics{
|
||||
{
|
||||
Login: "cleanup-expert",
|
||||
CommitCount: 50,
|
||||
CommentLinesAdded: 100,
|
||||
CommentLinesDeleted: 3000, // Should earn all deletion tiers
|
||||
RepositoriesContributed: []string{"owner/repo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
result := calc.Calculate(metrics)
|
||||
|
||||
contributor := result.Repositories[0].Contributors[0]
|
||||
// Should have all comment cleanup achievements
|
||||
assert.Contains(t, contributor.Achievements, "docs-del-50")
|
||||
assert.Contains(t, contributor.Achievements, "docs-del-200")
|
||||
assert.Contains(t, contributor.Achievements, "docs-del-500")
|
||||
assert.Contains(t, contributor.Achievements, "docs-del-1000")
|
||||
assert.Contains(t, contributor.Achievements, "docs-del-2500")
|
||||
})
|
||||
|
||||
t.Run("aggregates comment lines across multiple repositories", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Scoring.Enabled = true
|
||||
calc := NewCalculator(cfg)
|
||||
|
||||
metrics := &models.GlobalMetrics{
|
||||
Repositories: []models.RepositoryMetrics{
|
||||
{
|
||||
FullName: "owner/repo1",
|
||||
Contributors: []models.ContributorMetrics{
|
||||
{
|
||||
Login: "multi-repo-doc",
|
||||
CommitCount: 5,
|
||||
CommentLinesAdded: 300,
|
||||
CommentLinesDeleted: 30,
|
||||
RepositoriesContributed: []string{"owner/repo1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
FullName: "owner/repo2",
|
||||
Contributors: []models.ContributorMetrics{
|
||||
{
|
||||
Login: "multi-repo-doc",
|
||||
CommitCount: 5,
|
||||
CommentLinesAdded: 300,
|
||||
CommentLinesDeleted: 30,
|
||||
RepositoriesContributed: []string{"owner/repo2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
result := calc.Calculate(metrics)
|
||||
|
||||
// Check leaderboard entry (aggregated)
|
||||
require.Len(t, result.Leaderboard, 1)
|
||||
entry := result.Leaderboard[0]
|
||||
// Aggregated: 300 + 300 = 600 comment lines added, 30 + 30 = 60 deleted
|
||||
assert.Contains(t, entry.Achievements, "docs-100")
|
||||
assert.Contains(t, entry.Achievements, "docs-500")
|
||||
assert.NotContains(t, entry.Achievements, "docs-1000", "600 < 1000")
|
||||
assert.Contains(t, entry.Achievements, "docs-del-50")
|
||||
assert.NotContains(t, entry.Achievements, "docs-del-200", "60 < 200")
|
||||
})
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
-2
@@ -8,9 +8,9 @@
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<script type="module" crossorigin src="./assets/index-C2QviOxm.js"></script>
|
||||
<script type="module" crossorigin src="./assets/index-R3eb927Q.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="./assets/chart-Bcjh2pZL.js">
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-CmyGiR94.css">
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-8XjWwD9J.css">
|
||||
</head>
|
||||
<body class="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 font-sans transition-colors duration-300">
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -217,6 +217,8 @@ func (r *Repository) FetchCommits(ctx context.Context, owner, name string, since
|
||||
Deletions: stats.Deletions,
|
||||
MeaningfulAdditions: stats.MeaningfulAdditions,
|
||||
MeaningfulDeletions: stats.MeaningfulDeletions,
|
||||
CommentAdditions: stats.CommentAdditions,
|
||||
CommentDeletions: stats.CommentDeletions,
|
||||
FilesChanged: stats.FilesChanged,
|
||||
Repository: fmt.Sprintf("%s/%s", owner, name),
|
||||
URL: fmt.Sprintf("https://github.com/%s/%s/commit/%s", owner, name, c.Hash.String()),
|
||||
@@ -245,6 +247,8 @@ type commitStats struct {
|
||||
Deletions int
|
||||
MeaningfulAdditions int
|
||||
MeaningfulDeletions int
|
||||
CommentAdditions int
|
||||
CommentDeletions int
|
||||
FilesChanged int
|
||||
HasTests bool
|
||||
}
|
||||
@@ -327,6 +331,8 @@ func (r *Repository) getCommitStats(c *object.Commit, testPatterns []string) com
|
||||
stats.Additions++
|
||||
if diff.IsMeaningfulLine(line) {
|
||||
stats.MeaningfulAdditions++
|
||||
} else if diff.IsCommentLine(line) && !diff.IsWhitespaceLine(line) {
|
||||
stats.CommentAdditions++
|
||||
}
|
||||
}
|
||||
case 2: // Delete
|
||||
@@ -334,6 +340,8 @@ func (r *Repository) getCommitStats(c *object.Commit, testPatterns []string) com
|
||||
stats.Deletions++
|
||||
if diff.IsMeaningfulLine(line) {
|
||||
stats.MeaningfulDeletions++
|
||||
} else if diff.IsCommentLine(line) && !diff.IsWhitespaceLine(line) {
|
||||
stats.CommentDeletions++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -727,9 +727,10 @@ func convertCommit(c *github.RepositoryCommit, owner, repo string) models.Commit
|
||||
}
|
||||
filesChanged = len(c.Files)
|
||||
|
||||
// Detect if commit includes tests and calculate meaningful line counts
|
||||
// Detect if commit includes tests and calculate meaningful/comment line counts
|
||||
hasTests := false
|
||||
var meaningfulAdditions, meaningfulDeletions int
|
||||
var commentAdditions, commentDeletions int
|
||||
|
||||
for _, f := range c.Files {
|
||||
filename := f.GetFilename()
|
||||
@@ -749,12 +750,14 @@ func convertCommit(c *github.RepositoryCommit, owner, repo string) models.Commit
|
||||
continue
|
||||
}
|
||||
|
||||
// Analyze file patch to get meaningful line counts
|
||||
// Analyze file patch to get meaningful and comment line counts
|
||||
patch := f.GetPatch()
|
||||
if patch != "" {
|
||||
stats := diff.AnalyzePatch(patch)
|
||||
meaningfulAdditions += stats.MeaningfulAdditions
|
||||
meaningfulDeletions += stats.MeaningfulDeletions
|
||||
commentAdditions += stats.CommentAdditions
|
||||
commentDeletions += stats.CommentDeletions
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,6 +776,8 @@ func convertCommit(c *github.RepositoryCommit, owner, repo string) models.Commit
|
||||
Deletions: deletions,
|
||||
MeaningfulAdditions: meaningfulAdditions,
|
||||
MeaningfulDeletions: meaningfulDeletions,
|
||||
CommentAdditions: commentAdditions,
|
||||
CommentDeletions: commentDeletions,
|
||||
FilesChanged: filesChanged,
|
||||
Repository: fmt.Sprintf("%s/%s", owner, repo),
|
||||
URL: c.GetHTMLURL(),
|
||||
|
||||
Reference in New Issue
Block a user