diff --git a/models/cloudbrain.go b/models/cloudbrain.go index 851ab2456..777c2edaa 100755 --- a/models/cloudbrain.go +++ b/models/cloudbrain.go @@ -1933,6 +1933,7 @@ func CreateCloudbrain(cloudbrain *Cloudbrain) (err error) { session.Commit() go IncreaseDatasetUseCount(cloudbrain.Uuid) + go OperateRepoAITaskNum(cloudbrain.RepoID, 1) return nil } @@ -2088,9 +2089,29 @@ func DeleteJob(job *Cloudbrain) error { func deleteJob(e Engine, job *Cloudbrain) error { _, err := e.ID(job.ID).Delete(job) + if err == nil { + go updateAITaskNumWhenDeleteJob(job) + } return err } +func updateAITaskNumWhenDeleteJob(job *Cloudbrain) { + repoId := job.RepoID + if repoId == 0 { + t := &Cloudbrain{} + _, tempErr := x.ID(job.ID).Unscoped().Get(t) + if tempErr != nil { + log.Error("updateAITaskNumWhenDeleteJob error.%v", tempErr) + return + } + repoId = t.RepoID + } + + if repoId > 0 { + go OperateRepoAITaskNum(repoId, -1) + } +} + func GetCloudbrainByName(jobName string) (*Cloudbrain, error) { cb := &Cloudbrain{JobName: jobName} return getRepoCloudBrain(cb) @@ -2293,7 +2314,6 @@ func RestartCloudbrain(old *Cloudbrain, new *Cloudbrain) (err error) { } go IncreaseDatasetUseCount(new.Uuid) - return nil } func CloudbrainAll(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) { diff --git a/models/list_options.go b/models/list_options.go index 0946917fe..d6d1dcf0d 100644 --- a/models/list_options.go +++ b/models/list_options.go @@ -10,6 +10,26 @@ import ( "xorm.io/xorm" ) +type AvailablePageSize int + +const ( + PageSize15 AvailablePageSize = 15 + PageSize30 AvailablePageSize = 30 + PageSize50 AvailablePageSize = 50 +) + +func (s AvailablePageSize) IsLegal() bool { + switch s { + case PageSize30, PageSize50, PageSize15: + return true + } + return false +} + +func (s AvailablePageSize) Int() int { + return int(s) +} + // ListOptions options to paginate results type ListOptions struct { PageSize int diff --git a/models/repo.go b/models/repo.go index 832e3fc37..e390ef70d 100755 --- a/models/repo.go +++ b/models/repo.go @@ -231,10 +231,43 @@ type Repository struct { CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` - Hot int64 `xorm:"-"` - Active int64 `xorm:"-"` - Alias string `xorm:"INDEX"` - LowerAlias string `xorm:"INDEX"` + Hot int64 `xorm:"-"` + Active int64 `xorm:"-"` + Alias string `xorm:"INDEX"` + LowerAlias string `xorm:"INDEX"` + AiTaskCnt int64 `xorm:"NOT NULL DEFAULT 0"` + ModelCnt int64 `xorm:"NOT NULL DEFAULT 0"` + DatasetCnt int64 `xorm:"NOT NULL DEFAULT 0"` + LastMonthVisits int64 `xorm:"NOT NULL DEFAULT 0"` + LastFourMonthCommits int64 `xorm:"NOT NULL DEFAULT 0"` +} + +// Repository4Card format for front display +type Repository4Card struct { + ID int64 + OwnerID int64 + OwnerName string + LowerName string + Name string + Alias string + NumWatches int + NumStars int + NumForks int + Description string + Topics []string + AiTaskCnt int64 + ModelCnt int64 + DatasetCnt int64 + CreatedUnix timeutil.TimeStamp + UpdatedUnix timeutil.TimeStamp + PrimaryLanguage *LanguageStat + RelAvatarLink string + Contributors []*ContributorInfo + IsPrivate bool + IsFork bool + IsMirror bool + IsOwnerPrivate bool + IsArchived bool } type RepositoryShow struct { @@ -243,6 +276,47 @@ type RepositoryShow struct { Alias string } +func (repo *Repository) ToCardFormat() *Repository4Card { + link := repo.RelAvatarLink() + var isOwnerPrivate bool + if repo.Owner != nil && repo.Owner.Visibility.IsPrivate() { + isOwnerPrivate = true + } + result := &Repository4Card{ + ID: repo.ID, + OwnerID: repo.OwnerID, + OwnerName: repo.OwnerName, + LowerName: repo.LowerName, + Name: repo.Name, + NumWatches: repo.NumWatches, + NumStars: repo.NumStars, + NumForks: repo.NumForks, + Description: repo.Description, + Topics: repo.Topics, + AiTaskCnt: repo.AiTaskCnt, + ModelCnt: repo.ModelCnt, + DatasetCnt: repo.DatasetCnt, + CreatedUnix: repo.CreatedUnix, + UpdatedUnix: repo.UpdatedUnix, + PrimaryLanguage: repo.PrimaryLanguage, + RelAvatarLink: link, + Alias: repo.Alias, + IsPrivate: repo.IsPrivate, + IsFork: repo.IsFork, + IsMirror: repo.IsMirror, + IsOwnerPrivate: isOwnerPrivate, + IsArchived: repo.IsArchived, + } + return result +} + +type ContributorInfo struct { + RelAvatarLink string + UserName string + Email string + CommitCnt int +} + // SanitizedOriginalURL returns a sanitized OriginalURL func (repo *Repository) SanitizedOriginalURL() string { if repo.OriginalURL == "" { @@ -2379,6 +2453,75 @@ func CheckRepoStats(ctx context.Context) error { } } // ***** END: Repository.NumForks ***** + + // ***** START: Repository.DatasetCnt ***** + desc = "repository count 'dataset_cnt'" + results, err = x.Query("SELECT repository.id FROM `repository` WHERE repository.dataset_cnt!=(select count(1) from attachment inner join dataset on attachment.dataset_id = dataset.id where dataset.repo_id = repository.id)") + if err != nil { + log.Error("Select %s: %v", desc, err) + } else { + for _, result := range results { + id := com.StrTo(result["id"]).MustInt64() + select { + case <-ctx.Done(): + log.Warn("CheckRepoStats: Cancelled") + return ErrCancelledf("during %s for repo ID %d", desc, id) + default: + } + log.Trace("Updating %s: %d", desc, id) + err = ResetRepoDatasetNum(id) + if err != nil { + log.Error("Update %s[%d]: %v", desc, id, err) + } + } + } + // ***** END: Repository.DatasetCnt ***** + + // ***** START: Repository.ModelCnt ***** + desc = "repository count 'model_cnt'" + results, err = x.Query("SELECT repository.id FROM `repository` WHERE repository.model_cnt!=(select count(1) from ai_model_manage where repository.id = ai_model_manage.repo_id and ai_model_manage.size > 0 )") + if err != nil { + log.Error("Select %s: %v", desc, err) + } else { + for _, result := range results { + id := com.StrTo(result["id"]).MustInt64() + select { + case <-ctx.Done(): + log.Warn("CheckRepoStats: Cancelled") + return ErrCancelledf("during %s for repo ID %d", desc, id) + default: + } + log.Trace("Updating %s: %d", desc, id) + err = ResetRepoModelNum(id) + if err != nil { + log.Error("Update %s[%d]: %v", desc, id, err) + } + } + } + // ***** END: Repository.ModelCnt ***** + + // ***** START: Repository.AiTaskCnt ***** + desc = "repository count 'ai_task_cnt'" + results, err = x.Query("SELECT repository.id FROM `repository` WHERE repository.ai_task_cnt!=(select count(1) from cloudbrain where repository.id = cloudbrain.repo_id and (cloudbrain.deleted_at is null or cloudbrain.deleted_at = '0001-01-01 00:00:00') )") + if err != nil { + log.Error("Select %s: %v", desc, err) + } else { + for _, result := range results { + id := com.StrTo(result["id"]).MustInt64() + select { + case <-ctx.Done(): + log.Warn("CheckRepoStats: Cancelled") + return ErrCancelledf("during %s for repo ID %d", desc, id) + default: + } + log.Trace("Updating %s: %d", desc, id) + err = ResetRepoAITaskNum(id) + if err != nil { + log.Error("Update %s[%d]: %v", desc, id, err) + } + } + } + // ***** END: Repository.AiTaskCnt ***** return nil } @@ -2775,3 +2918,85 @@ func ReadLatestFileInRepo(userName, repoName, refName, treePath string) (*RepoFi } return &RepoFile{CommitId: commitId, Content: d}, nil } + +func ResetRepoAITaskNum(repoId int64) error { + n, err := x.Where("repo_id = ? ", repoId).Count(&Cloudbrain{}) + if err != nil { + return err + } + r := Repository{ + AiTaskCnt: n, + } + _, err = x.Cols("ai_task_cnt").Where("id = ?", repoId).Update(&r) + return err +} + +func ResetRepoDatasetNum(repoId int64) error { + n, err := x.Table("attachment").Join("inner", "dataset", "attachment.dataset_id = dataset.id").Where("dataset.repo_id = ?", repoId).Count() + if err != nil { + return err + } + r := Repository{ + DatasetCnt: n, + } + _, err = x.Cols("dataset_cnt").Where("id = ?", repoId).Update(&r) + return err +} + +func ResetRepoModelNum(repoId int64) error { + _, err := x.Exec("update repository set model_cnt = (select count(1) from ai_model_manage where ai_model_manage.repo_id = ? and size > 0) where id = ?", repoId, repoId) + return err +} + +func operateRepoCol(repoId int64, colName string, amount int64, engines ...*xorm.Engine) error { + var err error + + if amount == 0 { + return nil + } + var ee *xorm.Engine + if len(engines) == 0 { + ee = x + } else { + ee = engines[0] + } + if amount > 0 { + _, err = ee.Exec(fmt.Sprintf("update repository set %s = %s + ? where id = ?", colName, colName), amount, repoId) + } else { + _, err = ee.Exec(fmt.Sprintf("update repository set %s = %s - ? where id = ?", colName, colName), -1*amount, repoId) + } + + return err +} + +func OperateRepoDatasetNum(repoId int64, amount int64, engines ...*xorm.Engine) error { + return operateRepoCol(repoId, "dataset_cnt", amount, engines...) +} + +func OperateRepoModelNum(repoId int64, amount int64, engines ...*xorm.Engine) error { + return operateRepoCol(repoId, "model_cnt", amount, engines...) +} + +func OperateRepoAITaskNum(repoId int64, amount int64, engines ...*xorm.Engine) error { + return operateRepoCol(repoId, "ai_task_cnt", amount, engines...) +} + +func UpdateRepositoryLastFourMonthCommits(repoID int64, amount int64) error { + _, err := x.Exec("update repository set last_four_month_commits = ? where id = ?", amount, repoID) + return err +} +func UpdateRepositoryLastMonthVisits(repoID int64, amount int64) error { + _, err := x.Exec("update repository set last_month_visits = ? where id = ?", amount, repoID) + return err +} + +func SyncStatDataToRepo(repo *Repository) { + //Save the visit number of repository in the last month + if lv, err := SumLastMonthNumVisits(repo.ID); err == nil { + UpdateRepositoryLastMonthVisits(repo.ID, lv) + } + //Save the commits number of repository in the last four month + if lc, err := SumLastFourMonthNumCommits(repo.ID); err == nil { + UpdateRepositoryLastFourMonthCommits(repo.ID, lc) + } +} diff --git a/models/repo_list.go b/models/repo_list.go index 92654c11c..3c655fbd9 100755 --- a/models/repo_list.go +++ b/models/repo_list.go @@ -201,29 +201,41 @@ func (s SearchOrderBy) String() string { return string(s) } +type FindReposResponse struct { + Repos []*Repository4Card + Page int + PageSize int + Total int64 +} + // Strings for sorting result const ( - SearchOrderByAlphabetically SearchOrderBy = "name ASC" - SearchOrderByAlphabeticallyReverse SearchOrderBy = "name DESC" - SearchOrderByLeastUpdated SearchOrderBy = "updated_unix ASC" - SearchOrderByRecentUpdated SearchOrderBy = "updated_unix DESC" - SearchOrderByOldest SearchOrderBy = "created_unix ASC" - SearchOrderByNewest SearchOrderBy = "created_unix DESC" - SearchOrderBySize SearchOrderBy = "size ASC" - SearchOrderBySizeReverse SearchOrderBy = "size DESC" - SearchOrderByID SearchOrderBy = "id ASC" - SearchOrderByIDReverse SearchOrderBy = "id DESC" - SearchOrderByStars SearchOrderBy = "num_stars ASC" - SearchOrderByStarsReverse SearchOrderBy = "num_stars DESC" - SearchOrderByForks SearchOrderBy = "num_forks ASC" - SearchOrderByForksReverse SearchOrderBy = "num_forks DESC" - SearchOrderByDownloadTimes SearchOrderBy = "download_times DESC" - SearchOrderByUseCount SearchOrderBy = "use_count ASC" - SearchOrderByUseCountReverse SearchOrderBy = "use_count DESC" - SearchOrderByHot SearchOrderBy = "(num_watches + num_stars + num_forks + clone_cnt) DESC" - SearchOrderByActive SearchOrderBy = "(num_issues + num_pulls + num_commit) DESC" - SearchOrderByWatches SearchOrderBy = "num_watches DESC" - SearchOrderByDefault SearchOrderBy = "recommend desc,num_stars DESC,updated_unix DESC" + SearchOrderByAlphabetically SearchOrderBy = "name ASC" + SearchOrderByAlphabeticallyReverse SearchOrderBy = "name DESC" + SearchOrderByLeastUpdated SearchOrderBy = "updated_unix ASC" + SearchOrderByRecentUpdated SearchOrderBy = "updated_unix DESC" + SearchOrderByOldest SearchOrderBy = "created_unix ASC" + SearchOrderByNewest SearchOrderBy = "created_unix DESC" + SearchOrderBySize SearchOrderBy = "size ASC" + SearchOrderBySizeReverse SearchOrderBy = "size DESC" + SearchOrderByID SearchOrderBy = "id ASC" + SearchOrderByIDReverse SearchOrderBy = "id DESC" + SearchOrderByStars SearchOrderBy = "num_stars ASC" + SearchOrderByStarsReverse SearchOrderBy = "num_stars DESC" + SearchOrderByForks SearchOrderBy = "num_forks ASC" + SearchOrderByForksReverse SearchOrderBy = "num_forks DESC" + SearchOrderByDownloadTimes SearchOrderBy = "download_times DESC" + SearchOrderByUseCount SearchOrderBy = "use_count ASC" + SearchOrderByUseCountReverse SearchOrderBy = "use_count DESC" + SearchOrderByHot SearchOrderBy = "(num_watches + num_stars + num_forks + clone_cnt) DESC" + SearchOrderByActive SearchOrderBy = "(num_issues + num_pulls + num_commit) DESC" + SearchOrderByWatches SearchOrderBy = "num_watches DESC" + SearchOrderByDefault SearchOrderBy = "recommend desc,num_stars DESC,updated_unix DESC" + SearchOrderByAiTaskCntReverse SearchOrderBy = "ai_task_cnt desc" + SearchOrderByModelCntReverse SearchOrderBy = "model_cnt desc" + SearchOrderByDatasetCntReverse SearchOrderBy = "dataset_cnt desc" + SearchOrderByLastMonthVisitsReverse SearchOrderBy = "last_month_visits desc" + SearchOrderByLastFourMonthCommitsReverse SearchOrderBy = "last_four_month_commits desc" ) // SearchRepositoryCondition creates a query condition according search repository options diff --git a/models/repo_statistic.go b/models/repo_statistic.go index 4f8f13ed7..ecdd77e57 100755 --- a/models/repo_statistic.go +++ b/models/repo_statistic.go @@ -200,3 +200,23 @@ func UpdateRepoStatVisits(repoStat *RepoStatistic) error { _, err := xStatistic.Exec(sql, repoStat.NumVisits, repoStat.RepoID, repoStat.Date) return err } + +func SumRepoStatColumn(begin, end time.Time, repoId int64, columnName string) (int64, error) { + res, err := xStatistic.Where("created_unix <= ? and created_unix >= ? and repo_id = ? ", end.Unix(), begin.Unix(), repoId).Sum(&RepoStatistic{}, columnName) + if err != nil { + return 0, err + } + return int64(res), nil +} + +func SumLastMonthNumVisits(repoId int64) (int64, error) { + end := time.Now() + begin := end.AddDate(0, 0, -30) + return SumRepoStatColumn(begin, end, repoId, "num_visits") +} + +func SumLastFourMonthNumCommits(repoId int64) (int64, error) { + end := time.Now() + begin := end.AddDate(0, 0, -120) + return SumRepoStatColumn(begin, end, repoId, "num_commits_added") +} diff --git a/models/repo_tag.go b/models/repo_tag.go index 730eb3f2a..4585a95b6 100644 --- a/models/repo_tag.go +++ b/models/repo_tag.go @@ -4,6 +4,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" "fmt" + "xorm.io/builder" ) type OfficialTag struct { @@ -166,3 +167,33 @@ func GetAllOfficialTags() ([]OfficialTag, error) { } return o, nil } + +type FindSelectedReposOpts struct { + ListOptions + OrgId int64 + OnlyPublic bool +} + +func GetSelectedRepos(opts FindSelectedReposOpts) ([]*Repository, error) { + if opts.Page < 1 { + opts.Page = 1 + } + var cond = builder.NewCond() + cond = cond.And(builder.Eq{"official_tag.code": "selected"}) + if opts.OrgId > 0 { + cond = cond.And(builder.Eq{"official_tag_repos.org_id": opts.OrgId}) + } + if opts.OnlyPublic { + cond = cond.And(builder.Eq{"repository.is_private": false}) + } + t := make([]*Repository, 0) + err := x.Join("inner", "official_tag_repos", "repository.id = official_tag_repos.repo_id"). + Join("inner", "official_tag", "official_tag.id = official_tag_repos.tag_id"). + Where(cond).OrderBy("repository.updated_unix desc").Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Find(&t) + + if err != nil { + return nil, err + } + + return t, nil +} diff --git a/models/topic.go b/models/topic.go index 0b19bc1f0..ea5698f4c 100644 --- a/models/topic.go +++ b/models/topic.go @@ -9,6 +9,7 @@ import ( "regexp" "strings" "unicode/utf8" + "xorm.io/xorm" "code.gitea.io/gitea/modules/timeutil" @@ -337,3 +338,16 @@ func GetOrgTopics(orgId int64) ([]Topic, error) { return result, nil } + +func UpdateRepoTopics(repoID int64, topicNames []string, sess ...*xorm.Engine) error { + e := x + if len(sess) > 0 { + e = sess[0] + } + if _, err := e.ID(repoID).Cols("topics").Update(&Repository{ + Topics: topicNames, + }); err != nil { + return err + } + return nil +} diff --git a/models/user.go b/models/user.go index c421455bc..dad252d92 100755 --- a/models/user.go +++ b/models/user.go @@ -198,6 +198,40 @@ type SearchOrganizationsOptions struct { All bool } +type User4Front struct { + ID int64 + LowerName string `xorm:"UNIQUE NOT NULL"` + Name string `xorm:"UNIQUE NOT NULL"` + FullName string + Email string `xorm:"NOT NULL"` + Language string `xorm:"VARCHAR(5)"` + Description string + RelAvatarLink string + NumMembers int + CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` + UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` +} + +func (u *User) ToFrontFormat() *User4Front { + uf := &User4Front{ + ID: u.ID, + LowerName: u.LowerName, + Name: u.Name, + FullName: u.FullName, + Email: u.Email, + Language: u.Language, + Description: u.Description, + CreatedUnix: u.CreatedUnix, + UpdatedUnix: u.UpdatedUnix, + NumMembers: u.NumMembers, + } + if !u.KeepEmailPrivate { + uf.Email = u.Email + } + uf.RelAvatarLink = u.RelAvatarLink() + return uf +} + // GenerateRandomAvatar generates a random avatar for user. func (u *User) IsBindWechat() bool { return u.WechatOpenId != "" diff --git a/models/user_business_analysis.go b/models/user_business_analysis.go index 6c247709e..ccefbf4f6 100644 --- a/models/user_business_analysis.go +++ b/models/user_business_analysis.go @@ -2443,3 +2443,9 @@ func GetContentFromPromote(url string) (string, error) { allLineStr := string(bytes) return allLineStr, nil } + +func QueryLast30DaysHighestIndexUsers(size int) ([]int64, error) { + userIds := make([]int64, 0) + err := xStatistic.Table("user_business_analysis_last30_day").Cols("id").OrderBy("user_index desc").Limit(size).Find(&userIds) + return userIds, err +} diff --git a/modules/redis/redis_key/repo_redis_key.go b/modules/redis/redis_key/repo_redis_key.go new file mode 100644 index 000000000..b2b7ccd0a --- /dev/null +++ b/modules/redis/redis_key/repo_redis_key.go @@ -0,0 +1,9 @@ +package redis_key + +import "fmt" + +const REPO_PREFIX = "repo" + +func RepoTopNContributors(repoId int64, N int) string { + return KeyJoin(REPO_PREFIX, fmt.Sprint(repoId), fmt.Sprint(N), "top_n_contributor") +} diff --git a/modules/setting/setting.go b/modules/setting/setting.go index bf7eb2352..68c2c641b 100755 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -677,6 +677,10 @@ var ( CloudbrainStoppedTitle string CloudbrainStoppedRemark string + //repo square config + IncubationSourceOrgName string + PaperRepoTopicName string + //nginx proxy PROXYURL string RadarMap = struct { @@ -1585,6 +1589,10 @@ func NewContext() { CloudbrainStoppedTitle = sec.Key("CLOUDBRAIN_STOPPED_TITLE").MustString("您好,您申请的算力资源已结束使用,任务已完成运行,状态为%s,请您关注运行结果") CloudbrainStoppedRemark = sec.Key("CLOUDBRAIN_STOPPED_REMARK").MustString("感谢您的耐心等待。") + sec = Cfg.Section("repo-square") + IncubationSourceOrgName = sec.Key("INCUBATION_ORG_NAME").MustString("OpenI") + PaperRepoTopicName = sec.Key("PAPER_REPO_TOPIC_NAME").MustString("openi-paper") + sec = Cfg.Section("point") CloudBrainPaySwitch = sec.Key("CLOUDBRAIN_PAY_SWITCH").MustBool(false) CloudBrainPayDelay = sec.Key("CLOUDBRAIN_PAY_DELAY").MustDuration(30 * time.Minute) diff --git a/package.json b/package.json index 7748f3de3..fa2c5327b 100644 --- a/package.json +++ b/package.json @@ -17,11 +17,12 @@ "core-js": "3.6.5", "css-loader": "3.5.3", "cssnano": "4.1.10", + "dayjs": "1.10.7", "domino": "2.1.5", "dropzone": "5.7.2", "echarts": "3.8.5", "element-ui": "2.15.5", - "esdk-obs-browserjs": "3.20.7", + "esdk-obs-browserjs": "3.22.3", "esdk-obs-nodejs": "3.20.11", "fast-glob": "3.2.2", "file-loader": "6.0.0", diff --git a/public/home/home.js b/public/home/home.js index 3f31c857f..8fb7b08b1 100755 --- a/public/home/home.js +++ b/public/home/home.js @@ -631,20 +631,12 @@ function displayRepo(json){ for (var i = 0, iLen = repos.length; i < iLen; i++) { if (i >= 4) break; var repo = repos[i]; - // ${repo["NumStars"]}${repo["NumForks"]}
html += `
-
- ${repo["Avatar"] ? `` : ``} +
+ ${repo["Avatar"] ? `` : ``} ${repo["Alias"]}
${repo["Description"]}
- `; - // if (repo["Topics"] != null) { - // for(var j = 0; j < repo["Topics"].length; j++){ - // var topic = repo["Topics"][j]; - // var url = "/explore/repos?q=" + (topic) + "&topic=" - // html += `${topic}`; - // } - // } + `; html += `
`; diff --git a/routers/admin/resources.go b/routers/admin/resources.go index feea7b69b..8db958ef5 100644 --- a/routers/admin/resources.go +++ b/routers/admin/resources.go @@ -307,3 +307,37 @@ func RefreshHistorySpec(ctx *context.Context) { r["total"] = total ctx.JSON(http.StatusOK, response.SuccessWithData(r)) } + +func RefreshReposHistoryCnt(ctx *context.Context) { + scope := ctx.Query("scope") + list := ctx.Query("list") + + var scopeAll = false + if scope == "all" { + scopeAll = true + } + var ids = make([]int64, 0) + if list != "" { + strs := strings.Split(list, "|") + for _, s := range strs { + i, err := strconv.ParseInt(s, 10, 64) + if err != nil { + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ids = append(ids, i) + } + + } + + total, success, err := resource.RefreshHistorySpec(scopeAll, ids) + if err != nil { + log.Error("RefreshHistorySpec error. %v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + r := make(map[string]interface{}, 0) + r["success"] = success + r["total"] = total + ctx.JSON(http.StatusOK, response.SuccessWithData(r)) +} diff --git a/routers/api/v1/repo/topic.go b/routers/api/v1/repo/topic.go index f4ff7a329..d2522c9ce 100644 --- a/routers/api/v1/repo/topic.go +++ b/routers/api/v1/repo/topic.go @@ -177,13 +177,25 @@ func AddTopic(ctx *context.APIContext) { return } - _, err = models.AddTopic(ctx.Repo.Repository.ID, topicName) + topic, err := models.AddTopic(ctx.Repo.Repository.ID, topicName) if err != nil { log.Error("AddTopic failed: %v", err) ctx.InternalServerError(err) return } - + found := false + topicNames := make([]string, len(topics)) + for i, t := range topics { + topicNames[i] = t.Name + if strings.EqualFold(topic.Name, t.Name) { + found = true + break + } + } + if !found && topic.Name != "" { + topicNames = append(topicNames, topic.Name) + } + models.UpdateRepoTopics(ctx.Repo.Repository.ID, topicNames) ctx.Status(http.StatusNoContent) } diff --git a/routers/home.go b/routers/home.go index 092b30fe3..40a41bd68 100755 --- a/routers/home.go +++ b/routers/home.go @@ -7,6 +7,7 @@ package routers import ( "bytes" + "code.gitea.io/gitea/routers/response" "encoding/json" "net/http" "strconv" @@ -43,6 +44,8 @@ const ( tplHomeTerm base.TplName = "terms" tplHomePrivacy base.TplName = "privacy" tplResoruceDesc base.TplName = "resource_desc" + tplRepoSquare base.TplName = "explore/repos/square" + tplRepoSearch base.TplName = "explore/repos/search" ) // Home render home page @@ -296,6 +299,109 @@ func ExploreRepos(ctx *context.Context) { }) } +func GetRepoSquarePage(ctx *context.Context) { + ctx.Data["SquareBanners"] = repository.GetBanners() + ctx.Data["SquareTopics"] = repository.GetTopics() + ctx.Data["SquareRecommendRepos"] = repository.GetRecommendRepos() + + repos, _ := repository.GetPreferredRepos() + ctx.Data["SquarePreferredRepos"] = repos + ctx.HTML(200, tplRepoSquare) +} +func GetRepoSearchPage(ctx *context.Context) { + ctx.Data["SquareTopics"] = repository.GetTopics() + ctx.HTML(200, tplRepoSearch) +} + +func RepoSquare(ctx *context.Context) { + var result []*models.Repository4Card + var err error + switch ctx.Query("type") { + case "preferred": + result, err = repository.GetPreferredRepos() + case "incubation": + result, err = repository.GetIncubationRepos() + case "hot-paper": + result, err = repository.GetHotPaperRepos() + default: + result, err = repository.GetPreferredRepos() + } + if err != nil { + ctx.JSON(http.StatusOK, response.ResponseError(err)) + return + } + resultMap := make(map[string]interface{}, 0) + resultMap["Repos"] = result + ctx.JSON(http.StatusOK, response.SuccessWithData(resultMap)) +} + +func ActiveUser(ctx *context.Context) { + var err error + var currentUserId int64 + if ctx.User != nil { + currentUserId = ctx.User.ID + } + result, err := repository.GetActiveUser4Square(currentUserId) + if err != nil { + log.Error("ActiveUser err. %v", err) + ctx.JSON(http.StatusOK, response.Success()) + return + } + resultMap := make(map[string]interface{}, 0) + resultMap["Users"] = result + ctx.JSON(http.StatusOK, response.SuccessWithData(resultMap)) +} +func ActiveOrg(ctx *context.Context) { + result, err := repository.GetActiveOrgs() + if err != nil { + log.Error("ActiveOrg err. %v", err) + ctx.JSON(http.StatusOK, response.Success()) + return + } + resultMap := make(map[string]interface{}, 0) + resultMap["Orgs"] = result + ctx.JSON(http.StatusOK, response.SuccessWithData(resultMap)) +} + +func RepoFind(ctx *context.Context) { + keyword := strings.Trim(ctx.Query("q"), " ") + topic := strings.Trim(ctx.Query("topic"), " ") + sort := strings.Trim(ctx.Query("sort"), " ") + page := ctx.QueryInt("page") + pageSize := ctx.QueryInt("pageSize") + if pageSize == 0 { + pageSize = 15 + } + if pageSize > 100 { + ctx.JSON(http.StatusOK, response.ServerError("pageSize illegal")) + return + } + if page <= 0 { + page = 1 + } + + var ownerID int64 + if ctx.User != nil && !ctx.User.IsAdmin { + ownerID = ctx.User.ID + } + + result, err := repository.FindRepos(repository.FindReposOptions{ + ListOptions: models.ListOptions{Page: page, PageSize: pageSize}, + Actor: ctx.User, + Sort: sort, + Keyword: keyword, + Topic: topic, + Private: ctx.User != nil, + OwnerID: ownerID, + }) + if err != nil { + log.Error("RepoFind error. %v", err) + ctx.JSON(http.StatusOK, response.ResponseError(err)) + return + } + ctx.JSON(http.StatusOK, response.SuccessWithData(result)) +} + func ExploreDatasets(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("explore") ctx.Data["PageIsExplore"] = true diff --git a/routers/private/internal.go b/routers/private/internal.go index 14b0f05de..0b8ae600a 100755 --- a/routers/private/internal.go +++ b/routers/private/internal.go @@ -6,6 +6,7 @@ package private import ( + "code.gitea.io/gitea/services/repository" "strings" "code.gitea.io/gitea/routers/admin" @@ -55,7 +56,9 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/task/history_handle/duration", repo.HandleTaskWithNoDuration) m.Post("/task/history_handle/aicenter", repo.HandleTaskWithAiCenter) m.Post("/resources/specification/handle_historical_task", admin.RefreshHistorySpec) + m.Post("/repos/cnt_stat/handle_historical_task", admin.RefreshHistorySpec) m.Post("/duration_statisctic/history_handle", repo.CloudbrainUpdateHistoryData) + m.Post("/square/repo/stat/refresh", repository.RefreshRepoStatData) }, CheckInternalToken) } diff --git a/routers/repo/ai_model_manage.go b/routers/repo/ai_model_manage.go index ecb9edd2d..d36ddab52 100644 --- a/routers/repo/ai_model_manage.go +++ b/routers/repo/ai_model_manage.go @@ -2,6 +2,7 @@ package repo import ( "archive/zip" + "code.gitea.io/gitea/services/repository" "encoding/json" "errors" "fmt" @@ -170,10 +171,17 @@ func updateStatus(id string, modelSize int64, status int, modelPath string, stat if len(statusDesc) > 400 { statusDesc = statusDesc[0:400] } + m, _ := models.QueryModelById(id) err := models.ModifyModelStatus(id, modelSize, status, modelPath, statusDesc) if err != nil { log.Info("update status error." + err.Error()) } + if m != nil { + if modelSize > 0 && m.Size == 0 { + go repository.ResetRepoModelNum(m.RepoId) + } + } + } func SaveNewNameModel(ctx *context.Context) { @@ -308,13 +316,14 @@ func getSize(files []storage.FileInfo) int64 { func UpdateModelSize(modeluuid string) { model, err := models.QueryModelById(modeluuid) if err == nil { + var size int64 if model.Type == models.TypeCloudBrainOne { if strings.HasPrefix(model.Path, setting.Attachment.Minio.Bucket+"/"+Model_prefix) { files, err := storage.GetAllObjectByBucketAndPrefixMinio(setting.Attachment.Minio.Bucket, model.Path[len(setting.Attachment.Minio.Bucket)+1:]) if err != nil { log.Info("Failed to query model size from minio. id=" + modeluuid) } - size := getSize(files) + size = getSize(files) models.ModifyModelSize(modeluuid, size) } } else if model.Type == models.TypeCloudBrainTwo { @@ -323,10 +332,13 @@ func UpdateModelSize(modeluuid string) { if err != nil { log.Info("Failed to query model size from obs. id=" + modeluuid) } - size := getSize(files) + size = getSize(files) models.ModifyModelSize(modeluuid, size) } } + if model.Size == 0 && size > 0 { + go repository.ResetRepoModelNum(model.RepoId) + } } else { log.Info("not found model,uuid=" + modeluuid) } @@ -441,13 +453,14 @@ func DeleteModelFile(ctx *context.Context) { fileName := ctx.Query("fileName") model, err := models.QueryModelById(id) if err == nil { + var totalSize int64 if model.ModelType == MODEL_LOCAL_TYPE { if model.Type == models.TypeCloudBrainOne { bucketName := setting.Attachment.Minio.Bucket objectName := model.Path[len(bucketName)+1:] + fileName log.Info("delete bucket=" + bucketName + " path=" + objectName) if strings.HasPrefix(model.Path, bucketName+"/"+Model_prefix) { - totalSize := storage.MinioGetFilesSize(bucketName, []string{objectName}) + totalSize = storage.MinioGetFilesSize(bucketName, []string{objectName}) err := storage.Attachments.DeleteDir(objectName) if err != nil { log.Info("Failed to delete model. id=" + id) @@ -467,7 +480,7 @@ func DeleteModelFile(ctx *context.Context) { objectName := model.Path[len(setting.Bucket)+1:] + fileName log.Info("delete bucket=" + setting.Bucket + " path=" + objectName) if strings.HasPrefix(model.Path, bucketName+"/"+Model_prefix) { - totalSize := storage.ObsGetFilesSize(bucketName, []string{objectName}) + totalSize = storage.ObsGetFilesSize(bucketName, []string{objectName}) err := storage.ObsRemoveObject(bucketName, objectName) if err != nil { log.Info("Failed to delete model. id=" + id) @@ -484,6 +497,9 @@ func DeleteModelFile(ctx *context.Context) { } } } + if (model.Size - totalSize) <= 0 { + go repository.ResetRepoModelNum(model.RepoId) + } } ctx.JSON(200, map[string]string{ "code": "0", @@ -552,6 +568,9 @@ func deleteModelByID(ctx *context.Context, id string) error { } } } + if model.Size > 0 { + go repository.ResetRepoModelNum(model.RepoId) + } } } return err diff --git a/routers/repo/attachment.go b/routers/repo/attachment.go index 240e78acc..e1de71345 100755 --- a/routers/repo/attachment.go +++ b/routers/repo/attachment.go @@ -29,6 +29,7 @@ import ( "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/worker" + repo_service "code.gitea.io/gitea/services/repository" gouuid "github.com/satori/go.uuid" ) @@ -180,6 +181,7 @@ func DeleteAttachment(ctx *context.Context) { ctx.Error(500, fmt.Sprintf("DeleteAttachment: %v", err)) return } + go repo_service.DecreaseRepoDatasetNum(attach.DatasetID) attachjson, _ := json.Marshal(attach) labelmsg.SendDeleteAttachToLabelSys(string(attachjson)) @@ -894,6 +896,7 @@ func CompleteMultipart(ctx *context.Context) { return } attachment.UpdateDatasetUpdateUnix() + go repo_service.IncreaseRepoDatasetNum(dataset.ID) repository, _ := models.GetRepositoryByID(dataset.RepoID) notification.NotifyOtherTask(ctx.User, repository, fmt.Sprint(repository.IsPrivate, attachment.IsPrivate), attachment.Name, models.ActionUploadAttachment) if attachment.DatasetID != 0 { diff --git a/routers/repo/cloudbrain_statistic.go b/routers/repo/cloudbrain_statistic.go index 6ff377491..de95babe9 100644 --- a/routers/repo/cloudbrain_statistic.go +++ b/routers/repo/cloudbrain_statistic.go @@ -14,7 +14,13 @@ import ( ) func CloudbrainDurationStatisticHour() { - if setting.IsCloudbrainTimingEnabled { + defer func() { + err := recover() + if err == nil { + return + } + }() + if setting.IsCloudbrainTimingEnabled { var statisticTime time.Time var count int64 recordDurationUpdateTime, err := models.GetDurationRecordUpdateTime() diff --git a/routers/repo/repo_statistic.go b/routers/repo/repo_statistic.go index 468e6fa85..c1a7954a7 100755 --- a/routers/repo/repo_statistic.go +++ b/routers/repo/repo_statistic.go @@ -166,6 +166,8 @@ func RepoStatisticDaily(date string) { repoStat.NumIssuesGrowth = repoStat.NumIssues - repoStatisticFourMonthsAgo.NumIssues } + models.SyncStatDataToRepo(repo) + if _, err = models.InsertRepoStat(&repoStat); err != nil { log.Error("InsertRepoStat failed(%s): %v", projectName, err) log.Error("failed statistic: %s", projectName) diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 546854ef1..063a20999 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -371,7 +371,18 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/images/custom", repo.GetCustomImages) m.Get("/images/star", repo.GetStarImages) - m.Get("/repos", routers.ExploreRepos) + m.Group("/repos", func() { + //m.Get("", routers.ExploreRepos) + m.Get("", routers.GetRepoSearchPage) + m.Group("/square", func() { + m.Get("", routers.GetRepoSquarePage) + m.Get("/tab", routers.RepoSquare) + m.Get("/active-user", routers.ActiveUser) + m.Get("/active-org", routers.ActiveOrg) + }) + + m.Get("/search", routers.RepoFind) + }) m.Get("/datasets", routers.ExploreDatasets) m.Get("/users", routers.ExploreUsers) m.Get("/organizations", routers.ExploreOrganizations) diff --git a/services/repository/contributor.go b/services/repository/contributor.go new file mode 100644 index 000000000..9a86b91dc --- /dev/null +++ b/services/repository/contributor.go @@ -0,0 +1,88 @@ +package repository + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/redis/redis_client" + "code.gitea.io/gitea/modules/redis/redis_key" + "encoding/json" + "github.com/patrickmn/go-cache" + "time" +) + +var repoContributorCache = cache.New(5*time.Minute, 1*time.Minute) + +type ContributorCacheVal struct { + Contributors []*models.ContributorInfo + Total int +} + +func GetRepoTopNContributors(repo *models.Repository, N int) ([]*models.ContributorInfo, int) { + val, _ := redis_client.Get(redis_key.RepoTopNContributors(repo.ID, N)) + if val != "" { + log.Debug("Get RepoTopNContributors from redis,repo.ID = %d value = %v", repo.ID, val) + temp := &ContributorCacheVal{} + json.Unmarshal([]byte(val), temp) + return temp.Contributors, temp.Total + } + + contributorInfos, total := getRepoTopNContributorsFromDisk(repo, N) + log.Debug("Get RepoTopNContributors from disk,repo.ID = %d ", repo.ID) + jsonVal, err := json.Marshal(&ContributorCacheVal{Contributors: contributorInfos, Total: total}) + if err == nil { + redis_client.Setex(redis_key.RepoTopNContributors(repo.ID, N), string(jsonVal), 2*time.Minute) + } + return contributorInfos, total +} + +func getRepoTopNContributorsFromDisk(repo *models.Repository, N int) ([]*models.ContributorInfo, int) { + contributorInfos := make([]*models.ContributorInfo, 0) + + branchName := GetDefaultBranchName(repo) + if branchName == "" { + return contributorInfos, 0 + } + + contributors, err := git.GetContributors(repo.RepoPath(), branchName) + if err == nil && contributors != nil { + contributorInfoHash := make(map[string]*models.ContributorInfo) + for _, c := range contributors { + if len(contributorInfos) >= N { + break + } + if c.Email == "" { + 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 = &models.ContributorInfo{ + user.RelAvatarLink(), user.Name, user.Email, c.CommitCnt, + } + 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 = &models.ContributorInfo{ + "", "", c.Email, c.CommitCnt, + } + contributorInfos = append(contributorInfos, newContributor) + contributorInfoHash[c.Email] = newContributor + } + } + } + } + return contributorInfos, len(contributors) +} diff --git a/services/repository/repository.go b/services/repository/repository.go index db25010ea..a5c7c2fc4 100644 --- a/services/repository/repository.go +++ b/services/repository/repository.go @@ -5,18 +5,19 @@ package repository import ( - "fmt" - "io/ioutil" - "net/http" - "os" - "strings" - "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" pull_service "code.gitea.io/gitea/services/pull" + "fmt" + "io/ioutil" + "net/http" + "os" + "strings" + "xorm.io/xorm" ) const SHELL_FLAG_ON = 1 @@ -328,3 +329,47 @@ func IsUploadFileInvalidErr(err error) bool { _, ok := err.(UploadFileInvalidErr) return ok } + +func IncreaseRepoDatasetNum(datasetID int64, engines ...*xorm.Engine) error { + dataset, err := models.GetDatasetByID(datasetID) + if err != nil { + return err + } + return models.OperateRepoDatasetNum(dataset.RepoID, 1, engines...) +} + +func IncreaseRepoModelNum(repoId int64, engines ...*xorm.Engine) error { + return models.OperateRepoModelNum(repoId, 1, engines...) +} + +func ResetRepoModelNum(repoId int64) error { + return models.ResetRepoModelNum(repoId) +} + +func DecreaseRepoDatasetNum(datasetID int64, engines ...*xorm.Engine) error { + dataset, err := models.GetDatasetByID(datasetID) + if err != nil { + return err + } + return models.OperateRepoDatasetNum(dataset.RepoID, -1, engines...) +} + +func DecreaseRepoModelNum(repoId int64, engines ...*xorm.Engine) error { + return models.OperateRepoModelNum(repoId, -1, engines...) +} + +func GetDefaultBranchName(repo *models.Repository) string { + gitRepo, err := git.OpenRepository(repo.RepoPath()) + if err != nil { + return "" + } + defer gitRepo.Close() + if len(repo.DefaultBranch) > 0 && gitRepo.IsBranchExist(repo.DefaultBranch) { + return repo.DefaultBranch + } + brs, _, err := gitRepo.GetBranches(0, 0) + if len(brs) > 0 { + return brs[0] + } + return "" +} diff --git a/services/repository/square.go b/services/repository/square.go new file mode 100644 index 000000000..d68e5b189 --- /dev/null +++ b/services/repository/square.go @@ -0,0 +1,315 @@ +package repository + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "encoding/json" + "github.com/patrickmn/go-cache" + "time" +) + +var repoSquareCache = cache.New(2*time.Minute, 1*time.Minute) + +const ( + RREFERED_CACHE = "PreferredRepos" + REPO_BANNER_CACHE = "RepoBanner" + TOPICS_CACHE = "RepoTopics" + RECOMMEND_CACHE = "RecommendRepos" +) + +func GetBanners() []map[string]string { + v, success := repoSquareCache.Get(REPO_BANNER_CACHE) + if success { + log.Debug("GetBanners from cache,value = %v", v) + if v == nil { + return nil + } + r := v.([]map[string]string) + return r + } + repoMap := getMapContent("repos/square_banner") + repoSquareCache.Set(REPO_BANNER_CACHE, repoMap, 1*time.Minute) + return repoMap +} + +func GetTopics() []string { + v, success := repoSquareCache.Get(TOPICS_CACHE) + if success { + log.Debug("GetTopics from cache,value = %v", v) + if v == nil { + return nil + } + r := v.([]string) + return r + } + topics := getArrayContent("repos/recommend_topics") + repoSquareCache.Set(TOPICS_CACHE, topics, 1*time.Minute) + return topics +} + +func getMapContent(fileName string) []map[string]string { + url := setting.RecommentRepoAddr + fileName + result, err := RecommendContentFromPromote(url) + remap := make([]map[string]string, 0) + if err == nil { + json.Unmarshal([]byte(result), &remap) + } + return remap +} + +func getArrayContent(fileName string) []string { + url := setting.RecommentRepoAddr + fileName + result, err := RecommendContentFromPromote(url) + r := make([]string, 0) + if err == nil { + json.Unmarshal([]byte(result), &r) + } + return r +} + +func GetRecommendRepos() []map[string]interface{} { + v, success := repoSquareCache.Get(RECOMMEND_CACHE) + if success { + log.Debug("GetRecommendRepos from cache,value = %v", v) + if v == nil { + return nil + } + r := v.([]map[string]interface{}) + return r + } + repoMap := getMapContent("home/projects") + r, _ := GetRecommendRepoFromPromote(repoMap) + repoSquareCache.Set(RECOMMEND_CACHE, r, 1*time.Minute) + return r +} + +func GetPreferredRepos() ([]*models.Repository4Card, error) { + v, success := repoSquareCache.Get(RREFERED_CACHE) + if success { + log.Debug("GetPreferredRepos from cache,value = %v", v) + if v == nil { + return nil, nil + } + r := v.([]*models.Repository4Card) + return r, nil + } + + repos, err := models.GetSelectedRepos(models.FindSelectedReposOpts{ + ListOptions: models.ListOptions{ + PageSize: 10, + Page: 1, + }, + OnlyPublic: true, + }) + if err != nil { + return nil, err + } + result := make([]*models.Repository4Card, len(repos)) + for i, r := range repos { + result[i] = r.ToCardFormat() + } + + repoSquareCache.Set(RREFERED_CACHE, result, 1*time.Minute) + return result, nil +} + +func GetIncubationRepos() ([]*models.Repository4Card, error) { + org, err := models.GetOrgByName(setting.IncubationSourceOrgName) + if models.IsErrOrgNotExist(err) { + return make([]*models.Repository4Card, 0), nil + } + if err != nil { + return nil, err + } + repos, err := models.GetSelectedRepos(models.FindSelectedReposOpts{ + ListOptions: models.ListOptions{ + PageSize: 10, + Page: 1, + }, + OrgId: org.ID, + OnlyPublic: true, + }) + if err != nil { + return nil, err + } + result := make([]*models.Repository4Card, len(repos)) + for i, r := range repos { + result[i] = r.ToCardFormat() + } + return result, nil +} + +func GetHotPaperRepos() ([]*models.Repository4Card, error) { + rlist, _, err := models.SearchRepository(&models.SearchRepoOptions{ + ListOptions: models.ListOptions{ + Page: 1, + PageSize: 10, + }, + OrderBy: models.SearchOrderByLastMonthVisitsReverse + "," + models.SearchOrderByRecentUpdated, + TopicOnly: true, + TopicName: setting.PaperRepoTopicName, + AllPublic: true, + }) + if err != nil { + return nil, err + } + result := make([]*models.Repository4Card, len(rlist)) + for i, r := range rlist { + result[i] = r.ToCardFormat() + } + return result, nil +} + +type FindReposOptions struct { + models.ListOptions + Actor *models.User + Sort string + Keyword string + Topic string + Private bool + OwnerID int64 +} + +func FindRepos(opts FindReposOptions) (*models.FindReposResponse, error) { + + var ( + repos []*models.Repository + count int64 + err error + orderBy models.SearchOrderBy + ) + + switch opts.Sort { + //1.近期热门:按最近1个月浏览量倒序排序,最近1个月浏览量>最近更新>项目名称升序 + case "mostpopular": + orderBy = models.SearchOrderByLastMonthVisitsReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically + //2.近期活跃:按提交增长量(最近4个月commit数)倒序排序,提交增长量>最近更新>项目名称升序。 + case "mostactive": + orderBy = models.SearchOrderByLastFourMonthCommitsReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically + //3.最近更新:按最近更新>项目名称升序排序。 + case "recentupdate": + orderBy = models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically + //4.最近创建:按项目创建时间排序,最近的排前面。最近创建>项目名称升序。 + case "newest": + orderBy = models.SearchOrderByNewest + "," + models.SearchOrderByAlphabetically + //5.点赞最多:按点赞数倒序排序。点赞数>最近更新>项目名称升序。 + case "moststars": + orderBy = models.SearchOrderByStarsReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically + //6.派生最多:按派生数倒序排序。派生数>最近更新>项目名称升序。 + case "mostforks": + orderBy = models.SearchOrderByForksReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically + //7.数据集最多:按项目包含的数据集文件数量倒序排序,数据集文件数>最近更新>项目名称升序。 + case "mostdatasets": + orderBy = models.SearchOrderByDatasetCntReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically + //8.AI任务最多:按项目包含的AI任务数量倒序排序,AI任务数>最近更新>项目名称升序。 + case "mostaitasks": + orderBy = models.SearchOrderByAiTaskCntReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically + //9.模型最多:按项目包含的模型数量倒序排序,模型大小为0则不统计。模型数>最近更新>项目名称升序。 + case "mostmodels": + orderBy = models.SearchOrderByModelCntReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically + + default: + orderBy = models.SearchOrderByLastMonthVisitsReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically + } + + repos, count, err = models.SearchRepository(&models.SearchRepoOptions{ + ListOptions: opts.ListOptions, + Actor: opts.Actor, + OrderBy: orderBy, + Private: opts.Private, + Keyword: opts.Keyword, + OwnerID: opts.OwnerID, + AllPublic: true, + AllLimited: true, + TopicName: opts.Topic, + IncludeDescription: setting.UI.SearchRepoDescription, + }) + if err != nil { + log.Error("FindRepos error when SearchRepository.%v", err) + return nil, err + } + result := make([]*models.Repository4Card, len(repos)) + for i, r := range repos { + t := r.ToCardFormat() + contributors, _ := GetRepoTopNContributors(r, 6) + t.Contributors = contributors + result[i] = t + } + + return &models.FindReposResponse{ + Repos: result, + Total: count, + Page: opts.Page, + PageSize: opts.PageSize, + }, nil +} + +type ActiveUser struct { + User *models.User4Front + Followed bool + ShowButton bool +} + +func GetActiveUser4Square(currentUserId int64) ([]*ActiveUser, error) { + result := make([]*ActiveUser, 0) + userIds, err := models.QueryLast30DaysHighestIndexUsers(5) + if err != nil { + log.Error("ActiveUser err. %v", err) + return result, err + } + if len(userIds) == 0 { + return result, nil + } + + users, err := models.GetUsersByIDs(userIds) + if err != nil { + return result, nil + } + usersMap := make(map[int64]*models.User) + for _, v := range users { + usersMap[v.ID] = v + } + + for i := 0; i < len(userIds); i++ { + userId := userIds[i] + user := usersMap[userId] + if user == nil { + continue + } + isFollowed := false + if currentUserId != 0 { + isFollowed = models.IsFollowing(currentUserId, userId) + } + a := &ActiveUser{ + Followed: isFollowed, + User: user.ToFrontFormat(), + ShowButton: currentUserId != userId, + } + result = append(result, a) + } + return result, nil +} + +func GetActiveOrgs() ([]*models.User4Front, error) { + orgScores, err := models.FindTopNOpenIOrgs(5) + if err != nil { + return nil, err + } + orgs := make([]*models.User4Front, len(orgScores)) + for i, v := range orgScores { + orgs[i] = v.ToFrontFormat() + } + return orgs, nil +} + +func RefreshRepoStatData() { + repos, err := models.GetAllRepositories() + if err != nil { + log.Error("RefreshRepoStatData GetAllRepositories failed: %v", err.Error()) + return + } + for _, repo := range repos { + models.SyncStatDataToRepo(repo) + } +} diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl index 5fb9c09d3..de55e6452 100755 --- a/templates/base/head_navbar.tmpl +++ b/templates/base/head_navbar.tmpl @@ -35,7 +35,7 @@
- {{.i18n.Tr "custom.head.project"}} + {{.i18n.Tr "custom.head.project"}} {{.i18n.Tr "custom.head.dataset"}} - {{.i18n.Tr "custom.head.project"}} + {{.i18n.Tr "custom.head.project"}} {{.i18n.Tr "custom.head.dataset"}} {{else if .IsLandingPageExplore}} - {{.i18n.Tr "home"}} + {{.i18n.Tr "home"}} {{else if .IsLandingPageOrganizations}} {{.i18n.Tr "home"}} {{end}} diff --git a/templates/base/head_navbar_fluid.tmpl b/templates/base/head_navbar_fluid.tmpl index 63291d6fb..8a4682e9d 100644 --- a/templates/base/head_navbar_fluid.tmpl +++ b/templates/base/head_navbar_fluid.tmpl @@ -32,7 +32,7 @@ - {{.i18n.Tr "custom.head.project"}} + {{.i18n.Tr "custom.head.project"}} {{.i18n.Tr "custom.head.dataset"}} - {{.i18n.Tr "custom.head.project"}} + {{.i18n.Tr "custom.head.project"}} {{.i18n.Tr "custom.head.dataset"}} {{else if .IsLandingPageExplore}} - {{.i18n.Tr "home"}} + {{.i18n.Tr "home"}} {{else if .IsLandingPageOrganizations}} {{.i18n.Tr "home"}} {{end}} diff --git a/templates/base/head_navbar_home.tmpl b/templates/base/head_navbar_home.tmpl index 334ef5a33..f6741b7c8 100644 --- a/templates/base/head_navbar_home.tmpl +++ b/templates/base/head_navbar_home.tmpl @@ -24,7 +24,7 @@ - {{.i18n.Tr "custom.head.project"}} + {{.i18n.Tr "custom.head.project"}} {{.i18n.Tr "custom.head.dataset"}} - {{.i18n.Tr "custom.head.project"}} + {{.i18n.Tr "custom.head.project"}} {{.i18n.Tr "custom.head.dataset"}} {{else if .IsLandingPageExplore}} - {{.i18n.Tr "home"}} + {{.i18n.Tr "home"}} {{else if .IsLandingPageOrganizations}} {{.i18n.Tr "home"}} {{end}} diff --git a/templates/base/head_navbar_pro.tmpl b/templates/base/head_navbar_pro.tmpl index 55a090128..9e1c1ebf7 100644 --- a/templates/base/head_navbar_pro.tmpl +++ b/templates/base/head_navbar_pro.tmpl @@ -34,7 +34,7 @@ - {{.i18n.Tr "custom.head.project"}} + {{.i18n.Tr "custom.head.project"}} {{.i18n.Tr "custom.head.dataset"}}