package stats import ( "sort" "time" "github.com/atridad/wrapped-cli/pkg/gitea" "github.com/atridad/wrapped-cli/pkg/models" ) type Analyzer struct { repos []gitea.GiteaRepo commits []models.Commit } func NewAnalyzer() *Analyzer { return &Analyzer{ repos: []gitea.GiteaRepo{}, commits: []models.Commit{}, } } func (a *Analyzer) AddRepos(repos []gitea.GiteaRepo) { a.repos = repos } func (a *Analyzer) AddCommits(commits []models.Commit) { a.commits = append(a.commits, commits...) } func (a *Analyzer) inferPrimaryEmail(username string) string { emailCount := make(map[string]int) for _, c := range a.commits { if c.AuthorEmail != "" { emailCount[c.AuthorEmail]++ } } var bestEmail string max := 0 for email, count := range emailCount { if count > max { max = count bestEmail = email } } return bestEmail } func (a *Analyzer) Generate(username string) *models.UserStats { primaryEmail := a.inferPrimaryEmail(username) var filteredCommits []models.Commit for _, commit := range a.commits { if primaryEmail != "" && commit.AuthorEmail == primaryEmail { filteredCommits = append(filteredCommits, commit) } } stats := &models.UserStats{ Username: username, TotalCommits: len(filteredCommits), TotalRepositories: len(a.repos), } stats.Languages = a.generateLanguageStats() stats.CommitsByWeekday = a.generateWeekdayStats(filteredCommits) stats.CommitsByMonth = a.generateMonthStats(filteredCommits) stats.AverageCommitsPerDay = a.calculateAverageCommitsPerDay(filteredCommits) if len(stats.CommitsByMonth) > 0 { stats.MostActiveMonth = stats.CommitsByMonth[0].Date.Format("January") } if len(stats.CommitsByWeekday) > 0 { stats.MostActiveDay = stats.CommitsByWeekday[0].Day } stats.TopRepositories = a.getTopRepositories(filteredCommits, 5) return stats } func (a *Analyzer) generateLanguageStats() []models.LanguageStats { langCount := make(map[string]int) for _, repo := range a.repos { if repo.Language != "" { langCount[repo.Language]++ } } var stats []models.LanguageStats for lang, count := range langCount { stats = append(stats, models.LanguageStats{ Language: lang, Count: count, }) } sort.Slice(stats, func(i, j int) bool { return stats[i].Count > stats[j].Count }) total := len(a.repos) for i := range stats { stats[i].Percent = float64(stats[i].Count) / float64(total) * 100 } return stats } func (a *Analyzer) generateWeekdayStats(commits []models.Commit) []models.DateStats { dayCount := make(map[time.Weekday]int) dayNames := [7]string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"} for _, commit := range commits { day := commit.Timestamp.Weekday() dayCount[day]++ } var stats []models.DateStats for i := 0; i < 7; i++ { day := time.Weekday(i) stats = append(stats, models.DateStats{ Day: dayNames[i], Count: dayCount[day], }) } sort.Slice(stats, func(i, j int) bool { return stats[i].Count > stats[j].Count }) return stats } func (a *Analyzer) generateMonthStats(commits []models.Commit) []models.DateStats { monthCount := make(map[string]int) monthKeys := []string{} for _, commit := range commits { monthStr := commit.Timestamp.Format("2006-01") monthCount[monthStr]++ } for k := range monthCount { monthKeys = append(monthKeys, k) } var stats []models.DateStats for _, monthStr := range monthKeys { t, _ := time.Parse("2006-01", monthStr) stats = append(stats, models.DateStats{ Date: t, Count: monthCount[monthStr], }) } sort.Slice(stats, func(i, j int) bool { return stats[i].Count > stats[j].Count }) return stats } func (a *Analyzer) calculateAverageCommitsPerDay(commits []models.Commit) float64 { if len(commits) == 0 { return 0 } if len(commits) < 2 { return float64(len(commits)) } sort.Slice(commits, func(i, j int) bool { return commits[i].Timestamp.Before(commits[j].Timestamp) }) firstDay := commits[0].Timestamp lastDay := commits[len(commits)-1].Timestamp daysDiff := lastDay.Sub(firstDay).Hours() / 24 if daysDiff == 0 { daysDiff = 1 } return float64(len(commits)) / daysDiff } func (a *Analyzer) getTopRepositories(commits []models.Commit, n int) []models.Repository { repoCommitCount := make(map[string]int) repoMap := make(map[string]models.Repository) for _, commit := range commits { repoCommitCount[commit.RepoName]++ } for _, repo := range a.repos { repoMap[repo.Name] = models.Repository{ ID: repo.ID, Name: repo.Name, FullName: repo.FullName, HTMLURL: repo.HTMLURL, Language: repo.Language, Topics: repo.Topics, } } type repoWithCount struct { repo models.Repository count int } var repos []repoWithCount for name, count := range repoCommitCount { if repo, exists := repoMap[name]; exists { repos = append(repos, repoWithCount{repo, count}) } } sort.Slice(repos, func(i, j int) bool { return repos[i].count > repos[j].count }) if len(repos) > n { repos = repos[:n] } result := make([]models.Repository, len(repos)) for i, rc := range repos { result[i] = rc.repo } return result }