| @@ -10,6 +10,26 @@ import ( | |||
| "xorm.io/xorm" | |||
| ) | |||
| type SelectedPageSize int | |||
| const ( | |||
| PageSize15 SelectedPageSize = 15 | |||
| PageSize30 SelectedPageSize = 30 | |||
| PageSize50 SelectedPageSize = 50 | |||
| ) | |||
| func (s SelectedPageSize) IsLegal() bool { | |||
| switch s { | |||
| case PageSize30, PageSize50, PageSize15: | |||
| return true | |||
| } | |||
| return false | |||
| } | |||
| func (s SelectedPageSize) Int() int { | |||
| return int(s) | |||
| } | |||
| // ListOptions options to paginate results | |||
| type ListOptions struct { | |||
| PageSize int | |||
| @@ -231,10 +231,15 @@ 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"` | |||
| } | |||
| type RepositoryShow struct { | |||
| @@ -201,29 +201,41 @@ func (s SearchOrderBy) String() string { | |||
| return string(s) | |||
| } | |||
| type FindReposResponse struct { | |||
| Repos RepositoryList | |||
| 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 | |||
| @@ -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") | |||
| } | |||
| @@ -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 | |||
| } | |||
| @@ -664,6 +664,10 @@ var ( | |||
| CloudbrainStoppedTitle string | |||
| CloudbrainStoppedRemark string | |||
| //repo square config | |||
| IncubationSourceOrgId int64 | |||
| PaperSourceOwnerId int64 | |||
| //nginx proxy | |||
| PROXYURL string | |||
| RadarMap = struct { | |||
| @@ -1557,6 +1561,10 @@ func NewContext() { | |||
| CloudbrainStoppedTitle = sec.Key("CLOUDBRAIN_STOPPED_TITLE").MustString("您好,您申请的算力资源已结束使用,任务已完成运行,状态为%s,请您关注运行结果") | |||
| CloudbrainStoppedRemark = sec.Key("CLOUDBRAIN_STOPPED_REMARK").MustString("感谢您的耐心等待。") | |||
| sec = Cfg.Section("repo-square") | |||
| IncubationSourceOrgId = sec.Key("INCUBATION_ORG_ID").MustInt64(9) | |||
| PaperSourceOwnerId = sec.Key("PAPER_OWNER_ID").MustInt64(36008) | |||
| sec = Cfg.Section("point") | |||
| CloudBrainPaySwitch = sec.Key("CLOUDBRAIN_PAY_SWITCH").MustBool(false) | |||
| CloudBrainPayDelay = sec.Key("CLOUDBRAIN_PAY_DELAY").MustDuration(30 * time.Minute) | |||
| @@ -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)) | |||
| } | |||
| @@ -7,6 +7,7 @@ package routers | |||
| import ( | |||
| "bytes" | |||
| "code.gitea.io/gitea/routers/response" | |||
| "net/http" | |||
| "strconv" | |||
| "strings" | |||
| @@ -295,6 +296,63 @@ func ExploreRepos(ctx *context.Context) { | |||
| }) | |||
| } | |||
| func RepoSquare(ctx *context.Context) { | |||
| var result []models.Repository | |||
| 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 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 := models.SelectedPageSize(ctx.QueryInt("pageSize")) | |||
| if !pageSize.IsLegal() { | |||
| 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.Int()}, | |||
| 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 | |||
| @@ -824,4 +882,4 @@ func HomePrivacy(ctx *context.Context) { | |||
| func HomeResoruceDesc(ctx *context.Context) { | |||
| ctx.HTML(200, tplResoruceDesc) | |||
| } | |||
| } | |||
| @@ -55,6 +55,7 @@ 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) | |||
| }, CheckInternalToken) | |||
| @@ -13,6 +13,12 @@ import ( | |||
| ) | |||
| func CloudbrainDurationStatisticHour() { | |||
| defer func() { | |||
| err := recover() | |||
| if err == nil { | |||
| return | |||
| } | |||
| }() | |||
| var statisticTime time.Time | |||
| var count int64 | |||
| recordDurationUpdateTime, err := models.GetDurationRecordUpdateTime() | |||
| @@ -166,6 +166,8 @@ func RepoStatisticDaily(date string) { | |||
| repoStat.NumIssuesGrowth = repoStat.NumIssues - repoStatisticFourMonthsAgo.NumIssues | |||
| } | |||
| SyncStatDataToRepo(repo) | |||
| if _, err = models.InsertRepoStat(&repoStat); err != nil { | |||
| log.Error("InsertRepoStat failed(%s): %v", projectName, err) | |||
| log.Error("failed statistic: %s", projectName) | |||
| @@ -284,6 +286,24 @@ func RepoStatisticDaily(date string) { | |||
| } | |||
| func SyncStatDataToRepo(repo *models.Repository) { | |||
| changed := false | |||
| //Save the visit number of repository in the last month | |||
| if lv, err := models.SumLastMonthNumVisits(repo.ID); err == nil { | |||
| repo.LastMonthVisits = lv | |||
| changed = true | |||
| } | |||
| //Save the commits number of repository in the last four month | |||
| if lc, err := models.SumLastFourMonthNumCommits(repo.ID); err == nil { | |||
| repo.LastFourMonthCommits = lc | |||
| changed = true | |||
| } | |||
| if changed { | |||
| models.UpdateRepository(repo, false) | |||
| } | |||
| } | |||
| func getDistinctProjectName(repo *models.Repository) string { | |||
| return repo.OwnerName + "/" + repo.Alias | |||
| } | |||
| @@ -370,7 +370,11 @@ 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("/square", routers.RepoSquare) | |||
| m.Get("/search", routers.RepoFind) | |||
| }) | |||
| m.Get("/datasets", routers.ExploreDatasets) | |||
| m.Get("/users", routers.ExploreUsers) | |||
| m.Get("/organizations", routers.ExploreOrganizations) | |||
| @@ -0,0 +1,135 @@ | |||
| package repository | |||
| import ( | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| ) | |||
| func GetPreferredRepos() ([]models.Repository, error) { | |||
| return models.GetSelectedRepos(models.FindSelectedReposOpts{ | |||
| ListOptions: models.ListOptions{ | |||
| PageSize: 10, | |||
| Page: 1, | |||
| }, | |||
| OnlyPublic: true, | |||
| }) | |||
| } | |||
| func GetIncubationRepos() ([]models.Repository, error) { | |||
| return models.GetSelectedRepos(models.FindSelectedReposOpts{ | |||
| ListOptions: models.ListOptions{ | |||
| PageSize: 10, | |||
| Page: 1, | |||
| }, | |||
| OrgId: setting.IncubationSourceOrgId, | |||
| OnlyPublic: true, | |||
| }) | |||
| } | |||
| func GetHotPaperRepos() ([]models.Repository, error) { | |||
| rlist, _, err := models.SearchRepository(&models.SearchRepoOptions{ | |||
| ListOptions: models.ListOptions{ | |||
| Page: 1, | |||
| PageSize: 10, | |||
| }, | |||
| OrderBy: models.SearchOrderByLastMonthVisitsReverse + "," + models.SearchOrderByRecentUpdated, | |||
| TopicOnly: true, | |||
| TopicName: "openi-paper", | |||
| AllPublic: true, | |||
| }) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| result := make([]models.Repository, len(rlist)) | |||
| for i, r := range rlist { | |||
| result[i] = *r | |||
| } | |||
| 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个月浏览量>最近更新>项目名称升序 | |||
| //2.近期活跃:按提交增长量(最近4个月commit数)倒序排序,提交增长量>最近更新>项目名称升序。 | |||
| //3.最近更新:按最近更新>项目名称升序排序。 | |||
| //4.最近创建:按项目创建时间排序,最近的排前面。最近创建>项目名称升序。 | |||
| //5.点赞最多:按点赞数倒序排序。点赞数>最近更新>项目名称升序。 | |||
| //6.派生最多:按派生数倒序排序。派生数>最近更新>项目名称升序。 | |||
| //7.数据集最多:按项目包含的数据集文件数量倒序排序,数据集文件数>最近更新>项目名称升序。 | |||
| //8.AI任务最多:按项目包含的AI任务数量倒序排序,AI任务数>最近更新>项目名称升序。 | |||
| //9.模型最多:按项目包含的模型数量倒序排序,模型大小为0则不统计。模型数>最近更新>项目名称升序。\ | |||
| //1.近期热门 | |||
| case "most_popular": | |||
| orderBy = models.SearchOrderByLastMonthVisitsReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically | |||
| //2.近期活跃 | |||
| case "most_active": | |||
| orderBy = models.SearchOrderByLastFourMonthCommitsReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically | |||
| //3.最近更新 | |||
| case "recent_update": | |||
| orderBy = models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically | |||
| //4.最近创建 | |||
| case "newest": | |||
| orderBy = models.SearchOrderByNewest + "," + models.SearchOrderByAlphabetically | |||
| //5.点赞最多 | |||
| case "most_stars": | |||
| orderBy = models.SearchOrderByStarsReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically | |||
| //6.派生最多 | |||
| case "most_forks": | |||
| orderBy = models.SearchOrderByForksReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically | |||
| //7.数据集最多 | |||
| case "most_datasets": | |||
| orderBy = models.SearchOrderByDatasetCntReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically | |||
| //8.AI任务最多 | |||
| case "most_ai_tasks": | |||
| orderBy = models.SearchOrderByAiTaskCntReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically | |||
| //9.模型最多 | |||
| case "most_models": | |||
| 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 | |||
| } | |||
| return &models.FindReposResponse{ | |||
| Repos: repos, | |||
| Total: count, | |||
| Page: opts.Page, | |||
| PageSize: opts.PageSize, | |||
| }, nil | |||
| } | |||