Git statistics in Activity tab (#4724)
* Initial implementation for git statistics in Activity tab * Create top user by commit count endpoint * Add UI and update src-d/go-git dependency * Add coloring * Fix typo * Move git activity stats data extraction to git module * Fix message * Add git code stats test
This commit is contained in:
parent
2933ae4e88
commit
1fa9662946
7 changed files with 306 additions and 8 deletions
|
@ -6,11 +6,22 @@ package models
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
|
||||
// ActivityAuthorData represents statistical git commit count data
|
||||
type ActivityAuthorData struct {
|
||||
Name string `json:"name"`
|
||||
Login string `json:"login"`
|
||||
AvatarLink string `json:"avatar_link"`
|
||||
Commits int64 `json:"commits"`
|
||||
}
|
||||
|
||||
// ActivityStats represets issue and pull request information.
|
||||
type ActivityStats struct {
|
||||
OpenedPRs PullRequestList
|
||||
|
@ -24,32 +35,97 @@ type ActivityStats struct {
|
|||
UnresolvedIssues IssueList
|
||||
PublishedReleases []*Release
|
||||
PublishedReleaseAuthorCount int64
|
||||
Code *git.CodeActivityStats
|
||||
}
|
||||
|
||||
// GetActivityStats return stats for repository at given time range
|
||||
func GetActivityStats(repoID int64, timeFrom time.Time, releases, issues, prs bool) (*ActivityStats, error) {
|
||||
stats := &ActivityStats{}
|
||||
func GetActivityStats(repo *Repository, timeFrom time.Time, releases, issues, prs, code bool) (*ActivityStats, error) {
|
||||
stats := &ActivityStats{Code: &git.CodeActivityStats{}}
|
||||
if releases {
|
||||
if err := stats.FillReleases(repoID, timeFrom); err != nil {
|
||||
if err := stats.FillReleases(repo.ID, timeFrom); err != nil {
|
||||
return nil, fmt.Errorf("FillReleases: %v", err)
|
||||
}
|
||||
}
|
||||
if prs {
|
||||
if err := stats.FillPullRequests(repoID, timeFrom); err != nil {
|
||||
if err := stats.FillPullRequests(repo.ID, timeFrom); err != nil {
|
||||
return nil, fmt.Errorf("FillPullRequests: %v", err)
|
||||
}
|
||||
}
|
||||
if issues {
|
||||
if err := stats.FillIssues(repoID, timeFrom); err != nil {
|
||||
if err := stats.FillIssues(repo.ID, timeFrom); err != nil {
|
||||
return nil, fmt.Errorf("FillIssues: %v", err)
|
||||
}
|
||||
}
|
||||
if err := stats.FillUnresolvedIssues(repoID, timeFrom, issues, prs); err != nil {
|
||||
if err := stats.FillUnresolvedIssues(repo.ID, timeFrom, issues, prs); err != nil {
|
||||
return nil, fmt.Errorf("FillUnresolvedIssues: %v", err)
|
||||
}
|
||||
if code {
|
||||
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("OpenRepository: %v", err)
|
||||
}
|
||||
code, err := gitRepo.GetCodeActivityStats(timeFrom, repo.DefaultBranch)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("FillFromGit: %v", err)
|
||||
}
|
||||
stats.Code = code
|
||||
}
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// GetActivityStatsTopAuthors returns top author stats for git commits for all branches
|
||||
func GetActivityStatsTopAuthors(repo *Repository, timeFrom time.Time, count int) ([]*ActivityAuthorData, error) {
|
||||
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("OpenRepository: %v", err)
|
||||
}
|
||||
code, err := gitRepo.GetCodeActivityStats(timeFrom, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("FillFromGit: %v", err)
|
||||
}
|
||||
if code.Authors == nil {
|
||||
return nil, nil
|
||||
}
|
||||
users := make(map[int64]*ActivityAuthorData)
|
||||
for k, v := range code.Authors {
|
||||
if len(k) == 0 {
|
||||
continue
|
||||
}
|
||||
u, err := GetUserByEmail(k)
|
||||
if u == nil || IsErrUserNotExist(err) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user, ok := users[u.ID]; !ok {
|
||||
users[u.ID] = &ActivityAuthorData{
|
||||
Name: u.DisplayName(),
|
||||
Login: u.LowerName,
|
||||
AvatarLink: u.AvatarLink(),
|
||||
Commits: v,
|
||||
}
|
||||
} else {
|
||||
user.Commits += v
|
||||
}
|
||||
}
|
||||
v := make([]*ActivityAuthorData, 0)
|
||||
for _, u := range users {
|
||||
v = append(v, u)
|
||||
}
|
||||
|
||||
sort.Slice(v[:], func(i, j int) bool {
|
||||
return v[i].Commits < v[j].Commits
|
||||
})
|
||||
|
||||
cnt := count
|
||||
if cnt > len(v) {
|
||||
cnt = len(v)
|
||||
}
|
||||
|
||||
return v[:cnt], nil
|
||||
}
|
||||
|
||||
// ActivePRCount returns total active pull request count
|
||||
func (stats *ActivityStats) ActivePRCount() int {
|
||||
return stats.OpenedPRCount() + stats.MergedPRCount()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue