Skip to content

Commit d5b8772

Browse files
grokifyclaude
andcommitted
feat(profile): add cross-period aggregation for MonthlyStats
Adds Repositories field to MonthlyStats for tracking repos with commits each month. Adds AggregateMonthlyStats function and AggregatedStats type for computing quarterly/yearly statistics from monthly data with unique repository deduplication. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent e27d4a3 commit d5b8772

1 file changed

Lines changed: 71 additions & 0 deletions

File tree

profile/activity.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,21 @@ type MonthlyStats struct {
177177
// Repository counts
178178
RepoCountContributed int `json:"repoCountContributed"`
179179
RepoCountCreated int `json:"repoCountCreated"`
180+
181+
// Repository lists for cross-period aggregation
182+
// Repositories contains all repos with commits this month (owner/repo format)
183+
Repositories []string `json:"repositories,omitempty"`
180184
}
181185

182186
// ToMonthlyStats converts MonthlyActivity to a JSON-serializable MonthlyStats.
183187
func (m *MonthlyActivity) ToMonthlyStats() MonthlyStats {
188+
// Extract repository names from CommitsByRepo map
189+
repos := make([]string, 0, len(m.CommitsByRepo))
190+
for repo := range m.CommitsByRepo {
191+
repos = append(repos, repo)
192+
}
193+
sort.Strings(repos) // Deterministic output
194+
184195
return MonthlyStats{
185196
Year: m.Year,
186197
Month: int(m.Month),
@@ -195,6 +206,7 @@ func (m *MonthlyActivity) ToMonthlyStats() MonthlyStats {
195206
NetAdditions: m.NetAdditions(),
196207
RepoCountContributed: m.CommitRepoCount(),
197208
RepoCountCreated: m.RepoCountCreated(),
209+
Repositories: repos,
198210
}
199211
}
200212

@@ -312,3 +324,62 @@ func (t *ActivityTimeline) GetMonthStats(year int, month time.Month) *MonthlySta
312324
stats := m.ToMonthlyStats()
313325
return &stats
314326
}
327+
328+
// AggregateMonthlyStats combines multiple MonthlyStats into an aggregate summary.
329+
// This is useful for computing quarterly or yearly statistics from monthly data.
330+
// The returned stats contain unique repositories across all input months.
331+
func AggregateMonthlyStats(months []MonthlyStats) AggregatedStats {
332+
agg := AggregatedStats{
333+
repoSet: make(map[string]struct{}),
334+
}
335+
336+
for _, m := range months {
337+
agg.Commits += m.Commits
338+
agg.Issues += m.Issues
339+
agg.PRs += m.PRs
340+
agg.Reviews += m.Reviews
341+
agg.Releases += m.Releases
342+
agg.Additions += m.Additions
343+
agg.Deletions += m.Deletions
344+
345+
for _, repo := range m.Repositories {
346+
agg.repoSet[repo] = struct{}{}
347+
}
348+
}
349+
350+
agg.NetAdditions = agg.Additions - agg.Deletions
351+
agg.UniqueRepoCount = len(agg.repoSet)
352+
353+
// Build sorted list of unique repos
354+
agg.UniqueRepositories = make([]string, 0, len(agg.repoSet))
355+
for repo := range agg.repoSet {
356+
agg.UniqueRepositories = append(agg.UniqueRepositories, repo)
357+
}
358+
sort.Strings(agg.UniqueRepositories)
359+
360+
return agg
361+
}
362+
363+
// AggregatedStats represents statistics aggregated across multiple months.
364+
type AggregatedStats struct {
365+
// Contribution counts (summed)
366+
Commits int `json:"commits"`
367+
Issues int `json:"issues"`
368+
PRs int `json:"prs"`
369+
Reviews int `json:"reviews"`
370+
Releases int `json:"releases"`
371+
372+
// Code changes (summed)
373+
Additions int `json:"additions"`
374+
Deletions int `json:"deletions"`
375+
NetAdditions int `json:"netAdditions"`
376+
377+
// Unique repository count across all months
378+
UniqueRepoCount int `json:"uniqueRepoCount"`
379+
380+
// List of unique repositories (owner/repo format, sorted)
381+
UniqueRepositories []string `json:"uniqueRepositories,omitempty"`
382+
383+
// Internal: used during aggregation
384+
repoSet map[string]struct{} `json:"-"`
385+
}

0 commit comments

Comments
 (0)