package stats import ( "sort" "time" "github.com/atridad/gitea-wrapped/pkg/gitea" "github.com/atridad/gitea-wrapped/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) Generate(username string) *models.UserStats { stats := &models.UserStats{ Username: username, TotalCommits: len(a.commits), TotalRepositories: len(a.repos), } stats.Languages = a.generateLanguageStats() stats.CommitsByWeekday = a.generateWeekdayStats() stats.CommitsByMonth = a.generateMonthStats() stats.AverageCommitsPerDay = a.calculateAverageCommitsPerDay() 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(5) return stats } // generateLanguageStats creates language statistics 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 } // generateWeekdayStats creates weekday statistics for commits func (a *Analyzer) generateWeekdayStats() []models.DateStats { dayCount := make(map[time.Weekday]int) dayNames := [7]string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"} for _, commit := range a.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 } // generateMonthStats creates monthly statistics for commits func (a *Analyzer) generateMonthStats() []models.DateStats { monthCount := make(map[string]int) monthKeys := []string{} monthMap := make(map[string]time.Time) for _, commit := range a.commits { monthStr := commit.Timestamp.Format("2006-01") monthCount[monthStr]++ if _, exists := monthMap[monthStr]; !exists { monthKeys = append(monthKeys, monthStr) monthMap[monthStr] = commit.Timestamp } } 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 } // calculateAverageCommitsPerDay calculates average commits per calendar day func (a *Analyzer) calculateAverageCommitsPerDay() float64 { if len(a.commits) == 0 { return 0 } if len(a.commits) < 2 { return float64(len(a.commits)) } sort.Slice(a.commits, func(i, j int) bool { return a.commits[i].Timestamp.Before(a.commits[j].Timestamp) }) firstDay := a.commits[0].Timestamp lastDay := a.commits[len(a.commits)-1].Timestamp daysDiff := lastDay.Sub(firstDay).Hours() / 24 if daysDiff == 0 { daysDiff = 1 } return float64(len(a.commits)) / daysDiff } // getTopRepositories returns the top N repositories by commit count func (a *Analyzer) getTopRepositories(n int) []models.Repository { repoCommitCount := make(map[string]int) repoMap := make(map[string]models.Repository) for _, commit := range a.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 }