You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

repo_dashbord.go 13 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. package repo
  2. import (
  3. "fmt"
  4. "net/http"
  5. "strconv"
  6. "time"
  7. "code.gitea.io/gitea/models"
  8. "code.gitea.io/gitea/modules/log"
  9. "code.gitea.io/gitea/modules/context"
  10. "code.gitea.io/gitea/modules/setting"
  11. )
  12. const DEFAULT_PAGE_SIZE = 10
  13. const DATE_FORMAT = "2006-01-02"
  14. type ProjectsPeriodData struct {
  15. RecordBeginTime string `json:"recordBeginTime"`
  16. LastUpdatedTime string `json:"lastUpdatedTime"`
  17. PageSize int `json:"pageSize"`
  18. TotalPage int `json:"totalPage"`
  19. TotalCount int64 `json:"totalCount"`
  20. PageRecords []*models.RepoStatistic `json:"pageRecords"`
  21. }
  22. type UserInfo struct {
  23. User string `json:"user"`
  24. Mode int `json:"mode"`
  25. PR int64 `json:"pr"`
  26. Commit int `json:"commit"`
  27. }
  28. type ProjectLatestData struct {
  29. RecordBeginTime string `json:"recordBeginTime"`
  30. LastUpdatedTime string `json:"lastUpdatedTime"`
  31. CreatTime string `json:"creatTime"`
  32. OpenI float64 `json:"openi"`
  33. Comment int64 `json:"comment"`
  34. View int64 `json:"view"`
  35. Download int64 `json:"download"`
  36. IssueClosedRatio float32 `json:"issueClosedRatio"`
  37. Impact float64 `json:"impact"`
  38. Completeness float64 `json:"completeness"`
  39. Liveness float64 `json:"liveness"`
  40. ProjectHealth float64 `json:"projectHealth"`
  41. TeamHealth float64 `json:"teamHealth"`
  42. Growth float64 `json:"growth"`
  43. Description string `json:"description"`
  44. Top10 []UserInfo `json:"top10"`
  45. }
  46. func RestoreForkNumber(ctx *context.Context) {
  47. repos, err := models.GetAllRepositories()
  48. if err != nil {
  49. log.Error("GetAllRepositories failed: %v", err.Error())
  50. return
  51. }
  52. for _, repo := range repos {
  53. models.RestoreRepoStatFork(int64(repo.NumForks), repo.ID)
  54. }
  55. ctx.JSON(http.StatusOK, struct{}{})
  56. }
  57. func GetAllProjectsPeriodStatistics(ctx *context.Context) {
  58. recordBeginTime, err := getRecordBeginTime()
  59. if err != nil {
  60. log.Error("Can not get record begin time", err)
  61. ctx.Error(http.StatusBadRequest, ctx.Tr("repo.record_begintime_get_err"))
  62. return
  63. }
  64. beginTime, endTime, err := getTimePeroid(ctx, recordBeginTime)
  65. if err != nil {
  66. log.Error("Parameter is wrong", err)
  67. ctx.Error(http.StatusBadRequest, ctx.Tr("repo.parameter_is_wrong"))
  68. return
  69. }
  70. q := ctx.QueryTrim("q")
  71. page := ctx.QueryInt("page")
  72. if page <= 0 {
  73. page = 1
  74. }
  75. pageSize := ctx.QueryInt("pagesize")
  76. if pageSize <= 0 {
  77. pageSize = DEFAULT_PAGE_SIZE
  78. }
  79. orderBy := getOrderBy(ctx)
  80. latestUpdatedTime, latestDate, err := models.GetRepoStatLastUpdatedTime()
  81. if err != nil {
  82. log.Error("Can not query the last updated time.", err)
  83. ctx.Error(http.StatusBadRequest, ctx.Tr("repo.last_update_time_error"))
  84. return
  85. }
  86. countSql := generateCountSql(beginTime, endTime, latestDate, q)
  87. total, err := models.CountRepoStatByRawSql(countSql)
  88. if err != nil {
  89. log.Error("Can not query total count.", err)
  90. ctx.Error(http.StatusBadRequest, ctx.Tr("repo.total_count_get_error"))
  91. return
  92. }
  93. projectsPeriodData := ProjectsPeriodData{
  94. RecordBeginTime: recordBeginTime.Format(DATE_FORMAT),
  95. PageSize: pageSize,
  96. TotalPage: getTotalPage(total, pageSize),
  97. TotalCount: total,
  98. LastUpdatedTime: latestUpdatedTime,
  99. PageRecords: models.GetRepoStatisticByRawSql(generatePageSql(beginTime, endTime, latestDate, q, orderBy, page, pageSize)),
  100. }
  101. ctx.JSON(http.StatusOK, projectsPeriodData)
  102. }
  103. func GetProjectLatestStatistics(ctx *context.Context) {
  104. repoId := ctx.Params(":id")
  105. recordBeginTime, err := getRecordBeginTime()
  106. if err != nil {
  107. log.Error("Can not get record begin time", err)
  108. ctx.Error(http.StatusBadRequest, ctx.Tr("repo.record_begintime_get_err"))
  109. return
  110. }
  111. latestUpdatedTime, latestDate, err := models.GetRepoStatLastUpdatedTime(repoId)
  112. repoIdInt, _ := strconv.ParseInt(repoId, 10, 64)
  113. repoStat, err := models.GetRepoStatisticByDateAndRepoId(latestDate, repoIdInt)
  114. if err != nil {
  115. log.Error("Can not get the repo statistics "+repoId, err)
  116. ctx.Error(http.StatusBadRequest, ctx.Tr("repo.get_repo_stat_error"))
  117. return
  118. }
  119. repository, err := models.GetRepositoryByID(repoIdInt)
  120. if err != nil {
  121. log.Error("Can not get the repo info "+repoId, err)
  122. ctx.Error(http.StatusBadRequest, ctx.Tr("repo.get_repo_info_error"))
  123. return
  124. }
  125. projectLatestData := ProjectLatestData{
  126. RecordBeginTime: recordBeginTime.Format(DATE_FORMAT),
  127. CreatTime: time.Unix(int64(repository.CreatedUnix), 0).Format(DATE_FORMAT),
  128. LastUpdatedTime: latestUpdatedTime,
  129. OpenI: repoStat.RadarTotal,
  130. Comment: repoStat.NumComments,
  131. View: repoStat.NumVisits,
  132. Download: repoStat.NumDownloads,
  133. IssueClosedRatio: repoStat.IssueFixedRate,
  134. Impact: repoStat.Impact,
  135. Completeness: repoStat.Completeness,
  136. Liveness: repoStat.Liveness,
  137. ProjectHealth: repoStat.ProjectHealth,
  138. TeamHealth: repoStat.TeamHealth,
  139. Growth: repoStat.Growth,
  140. Description: repository.Description,
  141. }
  142. contributors, err := models.GetTop10Contributor(repository.RepoPath())
  143. if err != nil {
  144. log.Error("can not get contributors", err)
  145. }
  146. users := make([]UserInfo, 0)
  147. for _, contributor := range contributors {
  148. mode := repository.GetCollaboratorMode(contributor.UserId)
  149. if mode == -1 {
  150. if contributor.IsAdmin {
  151. mode = int(models.AccessModeAdmin)
  152. }
  153. if contributor.UserId == repository.OwnerID {
  154. mode = int(models.AccessModeOwner)
  155. }
  156. }
  157. pr := models.GetPullCountByUserAndRepoId(repoIdInt, contributor.UserId)
  158. userInfo := UserInfo{
  159. User: contributor.Committer,
  160. Commit: contributor.CommitCnt,
  161. Mode: mode,
  162. PR: pr,
  163. }
  164. users = append(users, userInfo)
  165. }
  166. projectLatestData.Top10 = users
  167. ctx.JSON(http.StatusOK, projectLatestData)
  168. }
  169. func GetProjectPeriodStatistics(ctx *context.Context) {
  170. repoId := ctx.Params(":id")
  171. recordBeginTime, err := getRecordBeginTime()
  172. if err != nil {
  173. log.Error("Can not get record begin time", err)
  174. ctx.Error(http.StatusBadRequest, ctx.Tr("repo.record_begintime_get_err"))
  175. return
  176. }
  177. repoIdInt, _ := strconv.ParseInt(repoId, 10, 64)
  178. if err != nil {
  179. log.Error("Can not get record begin time", err)
  180. ctx.Error(http.StatusBadRequest, ctx.Tr("repo.record_begintime_get_err"))
  181. return
  182. }
  183. beginTime, endTime, err := getTimePeroid(ctx, recordBeginTime)
  184. isOpenI := ctx.QueryBool("openi")
  185. var repositorys []*models.RepoStatistic
  186. if isOpenI {
  187. repositorys = models.GetRepoStatisticByRawSql(generateRadarSql(beginTime, endTime, repoIdInt))
  188. } else {
  189. repositorys = models.GetRepoStatisticByRawSql(generateTargetSql(beginTime, endTime, repoIdInt))
  190. }
  191. ctx.JSON(http.StatusOK, repositorys)
  192. }
  193. func generateRadarSql(beginTime time.Time, endTime time.Time, repoId int64) string {
  194. sql := "SELECT date, impact, completeness, liveness, project_health, team_health, growth, radar_total FROM repo_statistic" +
  195. " where repo_id=" + strconv.FormatInt(repoId, 10) + " and created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) +
  196. " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10)
  197. return sql
  198. }
  199. func generateTargetSql(beginTime time.Time, endTime time.Time, repoId int64) string {
  200. sql := "SELECT date, num_visits,num_downloads,num_commits FROM repo_statistic" +
  201. " where repo_id=" + strconv.FormatInt(repoId, 10) + " and created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) +
  202. " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10)
  203. return sql
  204. }
  205. func generateCountSql(beginTime time.Time, endTime time.Time, yesterday string, q string) string {
  206. countSql := "SELECT count(*) FROM " +
  207. "(SELECT repo_id FROM repo_statistic where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) +
  208. " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + " group by repo_id) A," +
  209. "(SELECT repo_id,name,is_private,radar_total from public.repo_statistic where date='" + yesterday + "') B" +
  210. " where A.repo_id=B.repo_id"
  211. if q != "" {
  212. countSql = countSql + " and B.name like '%" + q + "%'"
  213. }
  214. return countSql
  215. }
  216. func generatePageSql(beginTime time.Time, endTime time.Time, yesterday string, q string, orderBy string, page int, pageSize int) string {
  217. countSql := "SELECT A.repo_id,name,is_private,radar_total,num_watches,num_visits,num_downloads,num_pulls,num_commits,num_stars,num_forks,num_issues,num_closed_issues,num_contributor FROM " +
  218. "(SELECT repo_id,sum(num_watches_added) as num_watches,sum(num_visits) as num_visits, sum(num_downloads_added) as num_downloads,sum(num_pulls_added) as num_pulls,sum(num_commits_added) as num_commits,sum(num_stars_added) as num_stars,sum(num_forks_added) num_forks,sum(num_issues_added) as num_issues,sum(num_closed_issues_added) as num_closed_issues,sum(num_contributor_added) as num_contributor " +
  219. " FROM repo_statistic where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) +
  220. " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + " group by repo_id) A," +
  221. "(SELECT repo_id,name,is_private,radar_total from public.repo_statistic where date='" + yesterday + "') B" +
  222. " where A.repo_id=B.repo_id"
  223. if q != "" {
  224. countSql = countSql + " and B.name like '%" + q + "%'"
  225. }
  226. countSql = countSql + " order by " + orderBy + " desc,A.repo_id" + " limit " + strconv.Itoa(pageSize) + " offset " + strconv.Itoa((page-1)*pageSize)
  227. return countSql
  228. }
  229. func getOrderBy(ctx *context.Context) string {
  230. orderBy := ""
  231. switch ctx.Query("sort") {
  232. case "openi":
  233. orderBy = "B.radar_total"
  234. case "view":
  235. orderBy = "A.num_visits"
  236. case "download":
  237. orderBy = "A.num_downloads"
  238. case "pr":
  239. orderBy = "A.num_pulls"
  240. case "commit":
  241. orderBy = "A.num_commits"
  242. case "watch":
  243. orderBy = "A.num_watches"
  244. case "star":
  245. orderBy = "A.num_stars"
  246. case "fork":
  247. orderBy = "A.num_forks"
  248. case "issue":
  249. orderBy = "A.num_issues"
  250. case "issue_closed":
  251. orderBy = "A.num_closed_issues"
  252. case "contributor":
  253. orderBy = "A.num_contributor"
  254. default:
  255. orderBy = "B.radar_total"
  256. }
  257. return orderBy
  258. }
  259. func getTimePeroid(ctx *context.Context, recordBeginTime time.Time) (time.Time, time.Time, error) {
  260. queryType := ctx.QueryTrim("type")
  261. now := time.Now()
  262. recordBeginTimeTemp := recordBeginTime.AddDate(0, 0, 1)
  263. beginTimeStr := ctx.QueryTrim("beginTime")
  264. endTimeStr := ctx.QueryTrim("endTime")
  265. var beginTime time.Time
  266. var endTime time.Time
  267. if queryType != "" {
  268. if queryType == "all" {
  269. beginTime = recordBeginTimeTemp
  270. endTime = now
  271. } else if queryType == "yesterday" {
  272. endTime = now
  273. beginTime = time.Date(endTime.Year(), endTime.Month(), endTime.Day(), 0, 0, 0, 0, now.Location())
  274. } else if queryType == "current_week" {
  275. beginTime = now.AddDate(0, 0, -int(time.Now().Weekday())+1)
  276. beginTime = time.Date(beginTime.Year(), beginTime.Month(), beginTime.Day(), 0, 0, 0, 0, now.Location())
  277. endTime = now
  278. } else if queryType == "current_month" {
  279. endTime = now
  280. beginTime = time.Date(endTime.Year(), endTime.Month(), 2, 0, 0, 0, 0, now.Location())
  281. } else if queryType == "monthly" {
  282. endTime = now
  283. beginTime = now.AddDate(0, -1, 1)
  284. beginTime = time.Date(beginTime.Year(), beginTime.Month(), beginTime.Day(), 0, 0, 0, 0, now.Location())
  285. } else if queryType == "current_year" {
  286. endTime = now
  287. beginTime = time.Date(endTime.Year(), 1, 2, 0, 0, 0, 0, now.Location())
  288. } else if queryType == "last_month" {
  289. lastMonthTime := now.AddDate(0, -1, 0)
  290. beginTime = time.Date(lastMonthTime.Year(), lastMonthTime.Month(), 2, 0, 0, 0, 0, now.Location())
  291. endTime = time.Date(now.Year(), now.Month(), 2, 0, 0, 0, 0, now.Location())
  292. } else {
  293. return now, now, fmt.Errorf("The value of type parameter is wrong.")
  294. }
  295. } else {
  296. if beginTimeStr == "" || endTimeStr == "" {
  297. //如果查询类型和开始时间结束时间都未设置,按queryType=all处理
  298. beginTime = recordBeginTimeTemp
  299. endTime = now
  300. } else {
  301. beginTime, err := time.Parse("2006-01-02", beginTimeStr)
  302. if err != nil {
  303. return now, now, err
  304. }
  305. endTime, err := time.Parse("2006-01-02", endTimeStr)
  306. if err != nil {
  307. return now, now, err
  308. }
  309. beginTime = beginTime.AddDate(0, 0, 1)
  310. endTime = endTime.AddDate(0, 0, 1)
  311. }
  312. }
  313. if beginTime.Before(recordBeginTimeTemp) {
  314. beginTime = recordBeginTimeTemp
  315. }
  316. return beginTime, endTime, nil
  317. }
  318. func getRecordBeginTime() (time.Time, error) {
  319. return time.Parse(DATE_FORMAT, setting.RadarMap.RecordBeginTime)
  320. }
  321. func getTotalPage(total int64, pageSize int) int {
  322. another := 0
  323. if int(total)%pageSize != 0 {
  324. another = 1
  325. }
  326. return int(total)/pageSize + another
  327. }