diff --git a/models/org.go b/models/org.go old mode 100644 new mode 100755 index 58afc5cb5..e8006d55f --- a/models/org.go +++ b/models/org.go @@ -182,6 +182,7 @@ func CreateOrganization(org, owner *User) (err error) { if _, err = sess.Insert(&OrgUser{ UID: owner.ID, OrgID: org.ID, + IsPublic: setting.Service.DefaultOrgMemberVisible, }); err != nil { return fmt.Errorf("insert org-user relation: %v", err) } diff --git a/models/repo.go b/models/repo.go index c8629875e..1a5cf122c 100755 --- a/models/repo.go +++ b/models/repo.go @@ -210,9 +210,12 @@ type Repository struct { Balance string `xorm:"NOT NULL DEFAULT '0'"` BlockChainStatus RepoBlockChainStatus `xorm:"NOT NULL DEFAULT 0"` - // git clone total count + // git clone and git pull total count CloneCnt int64 `xorm:"NOT NULL DEFAULT 0"` + // only git clone total count + GitCloneCnt int64 `xorm:"NOT NULL DEFAULT 0"` + CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` @@ -2473,6 +2476,24 @@ func (repo *Repository) IncreaseCloneCnt() { return } +func (repo *Repository) IncreaseGitCloneCnt() { + sess := x.NewSession() + defer sess.Close() + + if err := sess.Begin(); err != nil { + return + } + if _, err := sess.Exec("UPDATE `repository` SET git_clone_cnt = git_clone_cnt + 1 WHERE id = ?", repo.ID); err != nil { + return + } + + if err := sess.Commit(); err != nil { + return + } + + return +} + func UpdateRepositoryCommitNum(repo *Repository) error { if _, err := x.Exec("UPDATE `repository` SET num_commit = ? where id = ?", repo.NumCommit, repo.ID); err != nil { return err diff --git a/models/repo_activity_custom.go b/models/repo_activity_custom.go index 698436e2a..7cfed2359 100644 --- a/models/repo_activity_custom.go +++ b/models/repo_activity_custom.go @@ -11,7 +11,8 @@ import ( type ContributorWithUserId struct { git.Contributor - UserId int64 + UserId int64 + IsAdmin bool } func GetRepoKPIStats(repo *Repository) (*git.RepoKPIStats, error) { @@ -144,6 +145,7 @@ func GetTop10Contributor(repoPath string) ([]ContributorWithUserId, error) { contributorDistinctDict[user.Email] = ContributorWithUserId{ contributor, user.ID, + user.IsAdmin, } } else { @@ -156,6 +158,7 @@ func GetTop10Contributor(repoPath string) ([]ContributorWithUserId, error) { contributorDistinctDict[contributor.Email] = ContributorWithUserId{ contributor, -1, + false, } } else { value.CommitCnt += contributor.CommitCnt diff --git a/models/repo_statistic.go b/models/repo_statistic.go index 9184ab4ec..df065bb79 100755 --- a/models/repo_statistic.go +++ b/models/repo_statistic.go @@ -158,6 +158,13 @@ func InsertRepoStat(repoStat *RepoStatistic) (int64, error) { return xStatistic.Insert(repoStat) } +func RestoreRepoStatFork(numForks int64, repoId int64) error { + sql := "update repo_statistic set num_forks=? where repo_id=?" + + _, err := xStatistic.Exec(sql, numForks, repoId) + return err +} + func UpdateRepoStat(repoStat *RepoStatistic) error { sql := "update repo_statistic set impact=?,completeness=?,liveness=?,project_health=?,team_health=?,growth=?,radar_total=? where repo_id=? and date=?" diff --git a/modules/cron/tasks_basic.go b/modules/cron/tasks_basic.go index ed9829cef..eac081a8f 100755 --- a/modules/cron/tasks_basic.go +++ b/modules/cron/tasks_basic.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/migrations" repository_service "code.gitea.io/gitea/modules/repository" + api_repo "code.gitea.io/gitea/routers/api/v1/repo" "code.gitea.io/gitea/routers/repo" mirror_service "code.gitea.io/gitea/services/mirror" ) @@ -195,6 +196,17 @@ func registerHandleUserStatistic() { }) } +func registerHandleClearRepoStatisticFile() { + RegisterTaskFatal("handle_repo_clear_statistic_file", &BaseConfig{ + Enabled: true, + RunAtStart: false, + Schedule: "@daily", + }, func(ctx context.Context, _ *models.User, _ Config) error { + api_repo.ClearUnusedStatisticsFile() + return nil + }) +} + func initBasicTasks() { registerUpdateMirrorTask() registerRepoHealthCheck() diff --git a/modules/setting/setting.go b/modules/setting/setting.go index abe406de8..9f31612b6 100755 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -163,6 +163,7 @@ var ( // UI settings UI = struct { ExplorePagingNum int + ContributorPagingNum int IssuePagingNum int RepoSearchPagingNum int MembersPagingNum int @@ -203,19 +204,20 @@ var ( Keywords string } `ini:"ui.meta"` }{ - ExplorePagingNum: 20, - IssuePagingNum: 10, - RepoSearchPagingNum: 10, - MembersPagingNum: 20, - FeedMaxCommitNum: 5, - GraphMaxCommitNum: 100, - CodeCommentLines: 4, - ReactionMaxUserNum: 10, - ThemeColorMetaTag: `#6cc644`, - MaxDisplayFileSize: 8388608, - DefaultTheme: `gitea`, - Themes: []string{`gitea`, `arc-green`}, - Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`}, + ExplorePagingNum: 20, + ContributorPagingNum: 50, + IssuePagingNum: 10, + RepoSearchPagingNum: 10, + MembersPagingNum: 20, + FeedMaxCommitNum: 5, + GraphMaxCommitNum: 100, + CodeCommentLines: 4, + ReactionMaxUserNum: 10, + ThemeColorMetaTag: `#6cc644`, + MaxDisplayFileSize: 8388608, + DefaultTheme: `gitea`, + Themes: []string{`gitea`, `arc-green`}, + Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`}, Notification: struct { MinTimeout time.Duration TimeoutStep time.Duration @@ -545,6 +547,7 @@ var ( GrowthCommit float64 GrowthComments float64 RecordBeginTime string + Path string }{} ) @@ -1325,7 +1328,8 @@ func SetRadarMapConfig() { RadarMap.GrowthContributors = sec.Key("growth_contributors").MustFloat64(0.2) RadarMap.GrowthCommit = sec.Key("growth_commit").MustFloat64(0.2) RadarMap.GrowthComments = sec.Key("growth_comments").MustFloat64(0.2) - RadarMap.RecordBeginTime = sec.Key("record_beigin_time").MustString("2021-11-04") + RadarMap.RecordBeginTime = sec.Key("record_beigin_time").MustString("2021-11-05") + RadarMap.Path = sec.Key("PATH").MustString("data/projectborad") } diff --git a/modules/storage/obs.go b/modules/storage/obs.go index bd73281d0..239580adb 100755 --- a/modules/storage/obs.go +++ b/modules/storage/obs.go @@ -10,11 +10,11 @@ import ( "strconv" "strings" - "github.com/unknwon/com" - "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/obs" "code.gitea.io/gitea/modules/setting" + + "github.com/unknwon/com" ) type FileInfo struct { @@ -178,30 +178,45 @@ func GetObsListObject(jobName, parentDir string) ([]FileInfo, error) { input := &obs.ListObjectsInput{} input.Bucket = setting.Bucket input.Prefix = strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, parentDir), "/") + strPrefix := strings.Split(input.Prefix, "/") output, err := ObsCli.ListObjects(input) fileInfos := make([]FileInfo, 0) if err == nil { for _, val := range output.Contents { str1 := strings.Split(val.Key, "/") var isDir bool - var fileName,nextParentDir string + var fileName, nextParentDir string if strings.HasSuffix(val.Key, "/") { + //dirs in next level dir + if len(str1)-len(strPrefix) > 2 { + continue + } fileName = str1[len(str1)-2] isDir = true - nextParentDir = fileName - if fileName == parentDir || (fileName + "/") == setting.OutPutPath { + if parentDir == "" { + nextParentDir = fileName + } else { + nextParentDir = parentDir + "/" + fileName + } + + if fileName == strPrefix[len(strPrefix)-1] || (fileName+"/") == setting.OutPutPath { continue } } else { + //files in next level dir + if len(str1)-len(strPrefix) > 1 { + continue + } fileName = str1[len(str1)-1] isDir = false + nextParentDir = parentDir } fileInfo := FileInfo{ - ModTime: val.LastModified.Format("2006-01-02 15:04:05"), + ModTime: val.LastModified.Local().Format("2006-01-02 15:04:05"), FileName: fileName, - Size: val.Size, - IsDir:isDir, + Size: val.Size, + IsDir: isDir, ParenDir: nextParentDir, } fileInfos = append(fileInfos, fileInfo) @@ -242,7 +257,7 @@ func GetObsCreateSignedUrl(jobName, parentDir, fileName string) (string, error) input := &obs.CreateSignedUrlInput{} input.Bucket = setting.Bucket input.Key = strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, parentDir, fileName), "/") - + input.Expires = 60 * 60 input.Method = obs.HttpMethodGet diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index a722ea671..bc71693b8 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -218,6 +218,7 @@ show_only_private = Showing only private show_only_public = Showing only public issues.in_your_repos = In your repositories +contributors = Contributors [explore] repos = Repositories @@ -2158,6 +2159,19 @@ repos.stars = Stars repos.forks = Forks repos.issues = Issues repos.size = Size +repos.id=ID +repos.projectName=Project Name +repos.isPrivate=Private +repos.openi=OpenI +repos.visit=Visit +repos.download=Code Download +repos.pr=PR +repos.commit=Commit +repos.closedIssues=Closed Issue +repos.contributor=Contributor +repos.yes=Yes +repos.no=No + datasets.dataset_manage_panel= Dataset Manage datasets.owner=Owner diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index e89c3dcde..a6d40355b 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -220,6 +220,8 @@ show_only_public=只显示公开的 issues.in_your_repos=属于该用户项目的 +contributors=贡献者 + [explore] repos=项目 users=用户 @@ -2160,6 +2162,19 @@ repos.stars=点赞数 repos.forks=派生数 repos.issues=任务数 repos.size=大小 +repos.id=ID +repos.projectName=项目名称 +repos.isPrivate=私有 +repos.openi=OpenI指数 +repos.visit=浏览量 +repos.download=代码下载量 +repos.pr=PR数 +repos.commit=Commit数 +repos.closedIssues=已解决任务数 +repos.contributor=贡献者数 +repos.yes=是 +repos.no=否 + datasets.dataset_manage_panel=数据集管理 datasets.owner=所有者 diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index dd744e933..b2e701998 100755 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -527,8 +527,11 @@ func RegisterRoutes(m *macaron.Macaron) { //Project board m.Group("/projectboard", func() { + m.Get("/restoreFork", adminReq, repo.RestoreForkNumber) + m.Group("/project", func() { m.Get("", adminReq, repo.GetAllProjectsPeriodStatistics) + m.Get("/download", adminReq, repo.ServeAllProjectsPeriodStatisticsFile) m.Group("/:id", func() { m.Get("", adminReq, repo.GetProjectLatestStatistics) m.Get("/period", adminReq, repo.GetProjectPeriodStatistics) diff --git a/routers/api/v1/repo/repo_dashbord.go b/routers/api/v1/repo/repo_dashbord.go index d9be6d5a7..4555d8a5c 100644 --- a/routers/api/v1/repo/repo_dashbord.go +++ b/routers/api/v1/repo/repo_dashbord.go @@ -1,8 +1,12 @@ package repo import ( + "encoding/csv" "fmt" + "io/ioutil" "net/http" + "os" + "path" "strconv" "time" @@ -21,6 +25,7 @@ type ProjectsPeriodData struct { LastUpdatedTime string `json:"lastUpdatedTime"` PageSize int `json:"pageSize"` TotalPage int `json:"totalPage"` + TotalCount int64 `json:"totalCount"` PageRecords []*models.RepoStatistic `json:"pageRecords"` } @@ -50,6 +55,19 @@ type ProjectLatestData struct { Top10 []UserInfo `json:"top10"` } +func RestoreForkNumber(ctx *context.Context) { + repos, err := models.GetAllRepositories() + if err != nil { + log.Error("GetAllRepositories failed: %v", err.Error()) + return + } + for _, repo := range repos { + models.RestoreRepoStatFork(int64(repo.NumForks), repo.ID) + } + + ctx.JSON(http.StatusOK, struct{}{}) +} + func GetAllProjectsPeriodStatistics(ctx *context.Context) { recordBeginTime, err := getRecordBeginTime() @@ -94,6 +112,7 @@ func GetAllProjectsPeriodStatistics(ctx *context.Context) { RecordBeginTime: recordBeginTime.Format(DATE_FORMAT), PageSize: pageSize, TotalPage: getTotalPage(total, pageSize), + TotalCount: total, LastUpdatedTime: latestUpdatedTime, PageRecords: models.GetRepoStatisticByRawSql(generatePageSql(beginTime, endTime, latestDate, q, orderBy, page, pageSize)), } @@ -102,6 +121,125 @@ func GetAllProjectsPeriodStatistics(ctx *context.Context) { } +func ServeAllProjectsPeriodStatisticsFile(ctx *context.Context) { + + recordBeginTime, err := getRecordBeginTime() + if err != nil { + log.Error("Can not get record begin time", err) + ctx.Error(http.StatusBadRequest, ctx.Tr("repo.record_begintime_get_err")) + return + } + beginTime, endTime, err := getTimePeroid(ctx, recordBeginTime) + if err != nil { + log.Error("Parameter is wrong", err) + ctx.Error(http.StatusBadRequest, ctx.Tr("repo.parameter_is_wrong")) + return + } + q := ctx.QueryTrim("q") + page := ctx.QueryInt("page") + if page <= 0 { + page = 1 + } + pageSize := 1000 + orderBy := getOrderBy(ctx) + + _, latestDate, err := models.GetRepoStatLastUpdatedTime() + if err != nil { + log.Error("Can not query the last updated time.", err) + ctx.Error(http.StatusBadRequest, ctx.Tr("repo.last_update_time_error")) + return + } + + countSql := generateCountSql(beginTime, endTime, latestDate, q) + total, err := models.CountRepoStatByRawSql(countSql) + if err != nil { + log.Error("Can not query total count.", err) + ctx.Error(http.StatusBadRequest, ctx.Tr("repo.total_count_get_error")) + return + } + + fileName := getFileName(ctx, beginTime, endTime) + + if err := os.MkdirAll(setting.RadarMap.Path, os.ModePerm); err != nil { + ctx.Error(http.StatusBadRequest, fmt.Errorf("Failed to create dir %s: %v", setting.AvatarUploadPath, err).Error()) + } + + totalPage := getTotalPage(total, pageSize) + + f, e := os.Create(fileName) + defer f.Close() + if e != nil { + log.Warn("Failed to create file", e) + } + writer := csv.NewWriter(f) + writer.Write(allProjectsPeroidHeader(ctx)) + for i := 0; i <= totalPage; i++ { + + pageRecords := models.GetRepoStatisticByRawSql(generatePageSql(beginTime, endTime, latestDate, q, orderBy, i+1, pageSize)) + for _, record := range pageRecords { + e = writer.Write(allProjectsPeroidValues(record, ctx)) + if e != nil { + log.Warn("Failed to write record", e) + } + } + writer.Flush() + + } + + ctx.ServeFile(fileName) + +} + +func getFileName(ctx *context.Context, beginTime time.Time, endTime time.Time) string { + baseName := setting.RadarMap.Path + "/" + if ctx.QueryTrim("type") != "" { + baseName = baseName + ctx.QueryTrim("type") + "_" + } + if ctx.QueryTrim("q") != "" { + baseName = baseName + ctx.QueryTrim("q") + "_" + } + baseName = baseName + beginTime.Format(DATE_FORMAT) + "_to_" + endTime.Format(DATE_FORMAT) + "_" + strconv.FormatInt(time.Now().Unix(), 10) + ".csv" + return baseName +} + +func ClearUnusedStatisticsFile() { + fileInfos, err := ioutil.ReadDir(setting.RadarMap.Path) + if err != nil { + log.Warn("can not read dir: "+setting.RadarMap.Path, err) + return + } + + for _, fileInfo := range fileInfos { + if !fileInfo.IsDir() && fileInfo.ModTime().Before(time.Now().AddDate(0, 0, -1)) { + os.Remove(path.Join(setting.RadarMap.Path, fileInfo.Name())) + } + } + +} + +func allProjectsPeroidHeader(ctx *context.Context) []string { + + return []string{ctx.Tr("repos.id"), ctx.Tr("repos.projectName"), ctx.Tr("repos.isPrivate"), ctx.Tr("repos.openi"), ctx.Tr("repos.visit"), ctx.Tr("repos.download"), ctx.Tr("repos.pr"), ctx.Tr("repos.commit"), + ctx.Tr("repos.watches"), ctx.Tr("repos.stars"), ctx.Tr("repos.forks"), ctx.Tr("repos.issues"), ctx.Tr("repos.closedIssues"), ctx.Tr("repos.contributor")} + +} + +func allProjectsPeroidValues(rs *models.RepoStatistic, ctx *context.Context) []string { + return []string{strconv.FormatInt(rs.RepoID, 10), rs.Name, getIsPrivateDisplay(rs.IsPrivate, ctx), strconv.FormatFloat(rs.RadarTotal, 'f', 2, 64), + strconv.FormatInt(rs.NumVisits, 10), strconv.FormatInt(rs.NumDownloads, 10), strconv.FormatInt(rs.NumPulls, 10), strconv.FormatInt(rs.NumCommits, 10), + strconv.FormatInt(rs.NumWatches, 10), strconv.FormatInt(rs.NumStars, 10), strconv.FormatInt(rs.NumForks, 10), strconv.FormatInt(rs.NumIssues, 10), + strconv.FormatInt(rs.NumClosedIssues, 10), strconv.FormatInt(rs.NumContributor, 10), + } +} + +func getIsPrivateDisplay(private bool, ctx *context.Context) string { + if private { + return ctx.Tr("repos.yes") + } else { + return ctx.Tr("repos.no") + } +} + func GetProjectLatestStatistics(ctx *context.Context) { repoId := ctx.Params(":id") recordBeginTime, err := getRecordBeginTime() @@ -151,6 +289,15 @@ func GetProjectLatestStatistics(ctx *context.Context) { for _, contributor := range contributors { mode := repository.GetCollaboratorMode(contributor.UserId) + if mode == -1 { + if contributor.IsAdmin { + mode = int(models.AccessModeAdmin) + } + if contributor.UserId == repository.OwnerID { + mode = int(models.AccessModeOwner) + } + } + pr := models.GetPullCountByUserAndRepoId(repoIdInt, contributor.UserId) userInfo := UserInfo{ User: contributor.Committer, diff --git a/routers/repo/http.go b/routers/repo/http.go index ed6276466..ad2abf567 100644 --- a/routers/repo/http.go +++ b/routers/repo/http.go @@ -317,6 +317,12 @@ func HTTP(ctx *context.Context) { go repo.IncreaseCloneCnt() } + if ctx.Req.Method == "POST" { + if strings.HasSuffix(ctx.Req.URL.Path, "git-upload-pack") { + go repo.IncreaseGitCloneCnt() + } + } + w := ctx.Resp r := ctx.Req.Request cfg := &serviceConfig{ diff --git a/routers/repo/repo_statistic.go b/routers/repo/repo_statistic.go index 6dbdc1ca2..c1edb0fed 100755 --- a/routers/repo/repo_statistic.go +++ b/routers/repo/repo_statistic.go @@ -93,6 +93,7 @@ func RepoStatisticDaily(date string) { IsPrivate: repo.IsPrivate, NumWatches: int64(repo.NumWatches), NumStars: int64(repo.NumStars), + NumForks: int64(repo.NumForks), NumDownloads: repo.CloneCnt, NumComments: numComments, NumVisits: int64(numVisits), diff --git a/routers/repo/user_data_analysis.go b/routers/repo/user_data_analysis.go index 7dc7af321..68cccd478 100755 --- a/routers/repo/user_data_analysis.go +++ b/routers/repo/user_data_analysis.go @@ -1,13 +1,27 @@ package repo import ( + "fmt" + "net/http" "time" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" ) +func QueryUserStaticData(ctx *context.Context) { + startDate := ctx.Query("startDate") + endDate := ctx.Query("endDate") + log.Info("startDate=" + startDate + " endDate=" + endDate) + startTime, _ := time.Parse("2006-01-02", startDate) + endTime, _ := time.Parse("2006-01-02", endDate) + log.Info("startTime=" + fmt.Sprint(startTime.Unix()) + " endDate=" + fmt.Sprint(endTime.Unix())) + ctx.JSON(http.StatusOK, models.QueryUserStaticData(startTime.Unix(), endTime.Unix())) + +} + func TimingCountDataByDate(date string) { t, _ := time.Parse("2006-01-02", date) diff --git a/routers/repo/view.go b/routers/repo/view.go index f85ab07cf..cd59ec920 100755 --- a/routers/repo/view.go +++ b/routers/repo/view.go @@ -12,6 +12,7 @@ import ( "fmt" gotemplate "html/template" "io/ioutil" + "net/http" "net/url" "path" "strings" @@ -31,11 +32,12 @@ import ( ) const ( - tplRepoEMPTY base.TplName = "repo/empty" - tplRepoHome base.TplName = "repo/home" - tplWatchers base.TplName = "repo/watchers" - tplForks base.TplName = "repo/forks" - tplMigrating base.TplName = "repo/migrating" + tplRepoEMPTY base.TplName = "repo/empty" + tplRepoHome base.TplName = "repo/home" + tplWatchers base.TplName = "repo/watchers" + tplForks base.TplName = "repo/forks" + tplMigrating base.TplName = "repo/migrating" + tplContributors base.TplName = "repo/contributors" ) type namedBlob struct { @@ -575,19 +577,29 @@ func safeURL(address string) string { } type ContributorInfo struct { - UserInfo *models.User // nil for contributor who is not a registered user - Email string - CommitCnt int + UserInfo *models.User // nil for contributor who is not a registered user + RelAvatarLink string `json:"rel_avatar_link"` + UserName string `json:"user_name"` + Email string `json:"email"` + CommitCnt int `json:"commit_cnt"` } -func getContributorInfo(contributorInfos []*ContributorInfo, email string) *ContributorInfo{ +type GetContributorsInfo struct { + ErrorCode int `json:"error_code"` + ErrorMsg string `json:"error_msg"` + Count int `json:"count"` + ContributorInfo []*ContributorInfo `json:"contributor_info"` +} + +func getContributorInfo(contributorInfos []*ContributorInfo, email string) *ContributorInfo { for _, c := range contributorInfos { - if strings.Compare(c.Email,email) == 0 { + if strings.Compare(c.Email, email) == 0 { return c } } return nil } + // Home render repository home page func Home(ctx *context.Context) { if len(ctx.Repo.Units) > 0 { @@ -596,35 +608,41 @@ func Home(ctx *context.Context) { if err == nil && contributors != nil { startTime := time.Now() var contributorInfos []*ContributorInfo - contributorInfoHash:= make(map[string]*ContributorInfo) + contributorInfoHash := make(map[string]*ContributorInfo) + count := 0 for _, c := range contributors { - if strings.Compare(c.Email,"") == 0 { + if count >= 25 { + continue + } + if strings.Compare(c.Email, "") == 0 { continue } // get user info from committer email user, err := models.GetUserByActivateEmail(c.Email) if err == nil { // committer is system user, get info through user's primary email - if existedContributorInfo,ok:=contributorInfoHash[user.Email];ok { + if existedContributorInfo, ok := contributorInfoHash[user.Email]; ok { // existed: same primary email, different committer name existedContributorInfo.CommitCnt += c.CommitCnt - }else{ + } else { // new committer info var newContributor = &ContributorInfo{ - user, user.Email,c.CommitCnt, + user, user.RelAvatarLink(), user.Name, user.Email, c.CommitCnt, } - contributorInfos = append(contributorInfos, newContributor ) + count++ + contributorInfos = append(contributorInfos, newContributor) contributorInfoHash[user.Email] = newContributor } } else { // committer is not system user - if existedContributorInfo,ok:=contributorInfoHash[c.Email];ok { + if existedContributorInfo, ok := contributorInfoHash[c.Email]; ok { // existed: same primary email, different committer name existedContributorInfo.CommitCnt += c.CommitCnt - }else{ + } else { var newContributor = &ContributorInfo{ - user, c.Email,c.CommitCnt, + user, "", "",c.Email, c.CommitCnt, } + count++ contributorInfos = append(contributorInfos, newContributor) contributorInfoHash[c.Email] = newContributor } @@ -632,7 +650,7 @@ func Home(ctx *context.Context) { } ctx.Data["ContributorInfo"] = contributorInfos var duration = time.Since(startTime) - log.Info("getContributorInfo cost: %v seconds",duration.Seconds()) + log.Info("getContributorInfo cost: %v seconds", duration.Seconds()) } if ctx.Repo.Repository.IsBeingCreated() { task, err := models.GetMigratingTask(ctx.Repo.Repository.ID) @@ -699,13 +717,13 @@ func renderLicense(ctx *context.Context) { log.Error("failed to get license content: %v, err:%v", f, err) continue } - if bytes.Compare(buf,license) == 0 { - log.Info("got matched license:%v",f) + if bytes.Compare(buf, license) == 0 { + log.Info("got matched license:%v", f) ctx.Data["LICENSE"] = f return } } - log.Info("not found matched license,repo:%v",ctx.Repo.Repository.Name) + log.Info("not found matched license,repo:%v", ctx.Repo.Repository.Name) } func renderLanguageStats(ctx *context.Context) { @@ -806,31 +824,31 @@ func renderCode(ctx *context.Context) { baseGitRepo, err := git.OpenRepository(ctx.Repo.Repository.BaseRepo.RepoPath()) defer baseGitRepo.Close() if err != nil { - log.Error("error open baseRepo:%s",ctx.Repo.Repository.BaseRepo.RepoPath()) + log.Error("error open baseRepo:%s", ctx.Repo.Repository.BaseRepo.RepoPath()) ctx.Data["FetchUpstreamCnt"] = -1 // minus value indicates error - }else{ - if _,error:= baseGitRepo.GetBranch(ctx.Repo.BranchName);error==nil{ + } else { + if _, error := baseGitRepo.GetBranch(ctx.Repo.BranchName); error == nil { //base repo has the same branch, then compare between current repo branch and base repo's branch - compareUrl := ctx.Repo.BranchName + "..." + ctx.Repo.Repository.BaseRepo.OwnerName + "/" + ctx.Repo.Repository.BaseRepo.Name + ":" + ctx.Repo.BranchName - ctx.SetParams("*",compareUrl) + compareUrl := ctx.Repo.BranchName + "..." + ctx.Repo.Repository.BaseRepo.OwnerName + "/" + ctx.Repo.Repository.BaseRepo.Name + ":" + ctx.Repo.BranchName + ctx.SetParams("*", compareUrl) ctx.Data["UpstreamSameBranchName"] = true - }else{ + } else { //else, compare between current repo branch and base repo's default branch - compareUrl := ctx.Repo.BranchName + "..." + ctx.Repo.Repository.BaseRepo.OwnerName + "/" + ctx.Repo.Repository.BaseRepo.Name + ":" + ctx.Repo.Repository.BaseRepo.DefaultBranch - ctx.SetParams("*",compareUrl) + compareUrl := ctx.Repo.BranchName + "..." + ctx.Repo.Repository.BaseRepo.OwnerName + "/" + ctx.Repo.Repository.BaseRepo.Name + ":" + ctx.Repo.Repository.BaseRepo.DefaultBranch + ctx.SetParams("*", compareUrl) ctx.Data["UpstreamSameBranchName"] = false } _, _, headGitRepo, compareInfo, _, _ := ParseCompareInfo(ctx) defer headGitRepo.Close() - if compareInfo!= nil { - if compareInfo.Commits!=nil { - log.Info("compareInfoCommits数量:%d",compareInfo.Commits.Len()) + if compareInfo != nil { + if compareInfo.Commits != nil { + log.Info("compareInfoCommits数量:%d", compareInfo.Commits.Len()) ctx.Data["FetchUpstreamCnt"] = compareInfo.Commits.Len() - }else{ + } else { log.Info("compareInfo nothing different") ctx.Data["FetchUpstreamCnt"] = 0 } - }else{ + } else { ctx.Data["FetchUpstreamCnt"] = -1 // minus value indicates error } } @@ -898,3 +916,64 @@ func Forks(ctx *context.Context) { ctx.HTML(200, tplForks) } + +func Contributors(ctx *context.Context) { + ctx.HTML(http.StatusOK, tplContributors) +} + +func ContributorsAPI(ctx *context.Context) { + count := 0 + errorCode := 0 + errorMsg := "" + contributors, err := git.GetContributors(ctx.Repo.Repository.RepoPath()) + var contributorInfos []*ContributorInfo + if err == nil && contributors != nil { + contributorInfoHash := make(map[string]*ContributorInfo) + for _, c := range contributors { + if strings.Compare(c.Email, "") == 0 { + continue + } + // get user info from committer email + user, err := models.GetUserByActivateEmail(c.Email) + if err == nil { + // committer is system user, get info through user's primary email + if existedContributorInfo, ok := contributorInfoHash[user.Email]; ok { + // existed: same primary email, different committer name + existedContributorInfo.CommitCnt += c.CommitCnt + } else { + // new committer info + var newContributor = &ContributorInfo{ + user, user.RelAvatarLink(),user.Name, user.Email,c.CommitCnt, + } + count++ + contributorInfos = append(contributorInfos, newContributor) + contributorInfoHash[user.Email] = newContributor + } + } else { + // committer is not system user + if existedContributorInfo, ok := contributorInfoHash[c.Email]; ok { + // existed: same primary email, different committer name + existedContributorInfo.CommitCnt += c.CommitCnt + } else { + var newContributor = &ContributorInfo{ + user, "", "",c.Email,c.CommitCnt, + } + count++ + contributorInfos = append(contributorInfos, newContributor) + contributorInfoHash[c.Email] = newContributor + } + } + } + } else { + log.Error("GetContributors failed: %v", err) + errorCode = -1 + errorMsg = err.Error() + } + + ctx.JSON(http.StatusOK, GetContributorsInfo{ + ErrorCode: errorCode, + ErrorMsg: errorMsg, + Count: count, + ContributorInfo: contributorInfos, + }) +} diff --git a/routers/routes/routes.go b/routers/routes/routes.go index a4b8f8aa1..383a308b6 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -791,9 +791,11 @@ func RegisterRoutes(m *macaron.Macaron) { }, reqSignIn, context.RepoAssignment(), context.UnitTypes(), reqRepoAdmin, context.RepoRef()) m.Post("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.UnitTypes(), repo.Action) - + m.Get("/tool/query_user_static", repo.QueryUserStaticData) // Grouping for those endpoints not requiring authentication m.Group("/:username/:reponame", func() { + m.Get("/contributors", repo.Contributors) + m.Get("/contributors/list", repo.ContributorsAPI) m.Group("/milestone", func() { m.Get("/:id", repo.MilestoneIssuesAndPulls) }, reqRepoIssuesOrPullsReader, context.RepoRef()) diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl index 24389d122..110200894 100755 --- a/templates/base/head.tmpl +++ b/templates/base/head.tmpl @@ -180,8 +180,8 @@ var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); - hm.src = "https://hm.baidu.com/hm.js?7c4ef0a24be6109ab22e63c832ab21cf"; - var s = document.getElementsByTagName("script")[0]; + hm.src = "https://hm.baidu.com/hm.js?46149a0b61fdeddfe427ff4de63794ba"; + var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })(); diff --git a/templates/base/head_home.tmpl b/templates/base/head_home.tmpl index 69bf23f51..d1b2934ea 100644 --- a/templates/base/head_home.tmpl +++ b/templates/base/head_home.tmpl @@ -181,8 +181,8 @@ var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); - hm.src = "https://hm.baidu.com/hm.js?7c4ef0a24be6109ab22e63c832ab21cf"; - var s = document.getElementsByTagName("script")[0]; + hm.src = "https://hm.baidu.com/hm.js?46149a0b61fdeddfe427ff4de63794ba"; + var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })(); diff --git a/templates/org/home.tmpl b/templates/org/home.tmpl index ad5a8e02d..7e9970d2b 100755 --- a/templates/org/home.tmpl +++ b/templates/org/home.tmpl @@ -39,7 +39,7 @@