| @@ -14,6 +14,8 @@ var customMigrations = []CustomMigration{ | |||||
| {"Custom v1 Topic struct change to support chinese", syncTopicStruct}, | {"Custom v1 Topic struct change to support chinese", syncTopicStruct}, | ||||
| } | } | ||||
| var customMigrationsStatic = []CustomMigration{} | |||||
| func MigrateCustom(x *xorm.Engine) { | func MigrateCustom(x *xorm.Engine) { | ||||
| for _, m := range customMigrations { | for _, m := range customMigrations { | ||||
| @@ -27,6 +29,17 @@ func MigrateCustom(x *xorm.Engine) { | |||||
| } | } | ||||
| func MigrateCustomStatic(x *xorm.Engine) { | |||||
| for _, m := range customMigrationsStatic { | |||||
| log.Info("Migration: %s", m.Description) | |||||
| if err := m.Migrate(x); err != nil { | |||||
| log.Error("Migration: %v", err) | |||||
| } | |||||
| } | |||||
| } | |||||
| func syncTopicStruct(x *xorm.Engine) error { | func syncTopicStruct(x *xorm.Engine) error { | ||||
| query := "ALTER TABLE topic ALTER COLUMN name TYPE varchar(105);" | query := "ALTER TABLE topic ALTER COLUMN name TYPE varchar(105);" | ||||
| @@ -190,7 +190,7 @@ func setEngine(engine *xorm.Engine, table []interface{}, database *setting.DBInf | |||||
| engine.SetMaxIdleConns(setting.Database.MaxIdleConns) | engine.SetMaxIdleConns(setting.Database.MaxIdleConns) | ||||
| engine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) | engine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) | ||||
| engine.Sync2(table...) | engine.Sync2(table...) | ||||
| MigrateCustom(engine) | |||||
| return nil | return nil | ||||
| } | } | ||||
| @@ -222,7 +222,7 @@ func NewEngine(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err e | |||||
| if err = newEngine(ctx, migrateFunc, x, tables, setting.Database); err != nil { | if err = newEngine(ctx, migrateFunc, x, tables, setting.Database); err != nil { | ||||
| return fmt.Errorf("newEngine failed: %v", err) | return fmt.Errorf("newEngine failed: %v", err) | ||||
| } | } | ||||
| MigrateCustom(x) | |||||
| xStatistic, err = getEngine(setting.DatabaseStatistic) | xStatistic, err = getEngine(setting.DatabaseStatistic) | ||||
| if err != nil { | if err != nil { | ||||
| return fmt.Errorf("Failed to connect to database: %v", err) | return fmt.Errorf("Failed to connect to database: %v", err) | ||||
| @@ -230,6 +230,7 @@ func NewEngine(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err e | |||||
| if err = newEngine(ctx, migrateFunc, xStatistic, tablesStatistic, setting.DatabaseStatistic); err != nil { | if err = newEngine(ctx, migrateFunc, xStatistic, tablesStatistic, setting.DatabaseStatistic); err != nil { | ||||
| return fmt.Errorf("newEngine statistic failed: %v", err) | return fmt.Errorf("newEngine statistic failed: %v", err) | ||||
| } | } | ||||
| MigrateCustomStatic(xStatistic) | |||||
| HasEngine = true | HasEngine = true | ||||
| @@ -5,7 +5,9 @@ import ( | |||||
| "time" | "time" | ||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/setting" | |||||
| "code.gitea.io/gitea/modules/timeutil" | "code.gitea.io/gitea/modules/timeutil" | ||||
| "xorm.io/builder" | |||||
| ) | ) | ||||
| type UserBusinessAnalysis struct { | type UserBusinessAnalysis struct { | ||||
| @@ -71,6 +73,14 @@ type UserBusinessAnalysis struct { | |||||
| Name string `xorm:"NOT NULL"` | Name string `xorm:"NOT NULL"` | ||||
| } | } | ||||
| type UserBusinessAnalysisQueryOptions struct { | |||||
| ListOptions | |||||
| UserName string | |||||
| SortType string | |||||
| StartTime int64 | |||||
| EndTime int64 | |||||
| } | |||||
| func QueryUserStaticData(startTime int64, endTime int64) []*UserBusinessAnalysis { | func QueryUserStaticData(startTime int64, endTime int64) []*UserBusinessAnalysis { | ||||
| log.Info("query startTime =" + fmt.Sprint(startTime) + " endTime=" + fmt.Sprint(endTime)) | log.Info("query startTime =" + fmt.Sprint(startTime) + " endTime=" + fmt.Sprint(endTime)) | ||||
| statictisSess := xStatistic.NewSession() | statictisSess := xStatistic.NewSession() | ||||
| @@ -114,11 +124,114 @@ func QueryUserStaticData(startTime int64, endTime int64) []*UserBusinessAnalysis | |||||
| return userBusinessAnalysisReturnList | return userBusinessAnalysisReturnList | ||||
| } | } | ||||
| func QueryUserStaticDataPage(opts *UserBusinessAnalysisQueryOptions) ([]*UserBusinessAnalysis, int64) { | |||||
| log.Info("query startTime =" + fmt.Sprint(opts.StartTime) + " endTime=" + fmt.Sprint(opts.EndTime)) | |||||
| statictisSess := xStatistic.NewSession() | |||||
| defer statictisSess.Close() | |||||
| currentTimeNow := time.Now() | |||||
| pageStartTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, currentTimeNow.Location()) | |||||
| pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 23, 59, 59, 0, currentTimeNow.Location()) | |||||
| var cond = builder.NewCond() | |||||
| if len(opts.UserName) > 0 { | |||||
| cond = cond.And( | |||||
| builder.Eq{"name": opts.UserName}, | |||||
| ) | |||||
| } | |||||
| cond = cond.And( | |||||
| builder.Gte{"count_date": fmt.Sprint(pageStartTime)}, | |||||
| ) | |||||
| cond = cond.And( | |||||
| builder.Lte{"count_date": fmt.Sprint(pageEndTime)}, | |||||
| ) | |||||
| count, err := statictisSess.Where(cond).Count(new(UserBusinessAnalysis)) | |||||
| if err != nil { | |||||
| log.Info("query error." + err.Error()) | |||||
| return nil, 0 | |||||
| } | |||||
| if opts.Page >= 0 && opts.PageSize > 0 { | |||||
| var start int | |||||
| if opts.Page == 0 { | |||||
| start = 0 | |||||
| } else { | |||||
| start = (opts.Page - 1) * opts.PageSize | |||||
| } | |||||
| statictisSess.Limit(opts.PageSize, start) | |||||
| } | |||||
| statictisSess.OrderBy("count_date desc") | |||||
| userBusinessAnalysisList := make([]*UserBusinessAnalysis, 0, setting.UI.IssuePagingNum) | |||||
| if err := statictisSess.Table("user_business_analysis").Where(cond). | |||||
| Find(&userBusinessAnalysisList); err != nil { | |||||
| return nil, 0 | |||||
| } | |||||
| resultMap := make(map[int64]*UserBusinessAnalysis) | |||||
| var newAndCond = builder.NewCond() | |||||
| var newOrCond = builder.NewCond() | |||||
| for _, userRecord := range userBusinessAnalysisList { | |||||
| newOrCond.Or( | |||||
| builder.Eq{"id": userRecord.ID}, | |||||
| ) | |||||
| } | |||||
| newAndCond = newAndCond.And( | |||||
| newOrCond, | |||||
| ) | |||||
| newAndCond = newAndCond.And( | |||||
| builder.Gte{"count_date": fmt.Sprint(opts.StartTime)}, | |||||
| ) | |||||
| newAndCond = newAndCond.And( | |||||
| builder.Lte{"count_date": fmt.Sprint(opts.EndTime)}, | |||||
| ) | |||||
| userBusinessAnalysisList = make([]*UserBusinessAnalysis, 0) | |||||
| if err := statictisSess.Table("user_business_analysis").Where(newAndCond). | |||||
| Find(&userBusinessAnalysisList); err != nil { | |||||
| return nil, 0 | |||||
| } | |||||
| log.Info("query result size=" + fmt.Sprint(len(userBusinessAnalysisList))) | |||||
| for _, userRecord := range userBusinessAnalysisList { | |||||
| if _, ok := resultMap[userRecord.ID]; !ok { | |||||
| resultMap[userRecord.ID] = userRecord | |||||
| } else { | |||||
| resultMap[userRecord.ID].CodeMergeCount += userRecord.CodeMergeCount | |||||
| resultMap[userRecord.ID].CommitCount += userRecord.CommitCount | |||||
| resultMap[userRecord.ID].IssueCount += userRecord.IssueCount | |||||
| resultMap[userRecord.ID].CommentCount += userRecord.CommentCount | |||||
| resultMap[userRecord.ID].FocusRepoCount += userRecord.FocusRepoCount | |||||
| resultMap[userRecord.ID].StarRepoCount += userRecord.StarRepoCount | |||||
| resultMap[userRecord.ID].WatchedCount += userRecord.WatchedCount | |||||
| resultMap[userRecord.ID].CommitCodeSize += userRecord.CommitCodeSize | |||||
| resultMap[userRecord.ID].CommitDatasetSize += userRecord.CommitDatasetSize | |||||
| resultMap[userRecord.ID].CommitModelCount += userRecord.CommitModelCount | |||||
| resultMap[userRecord.ID].SolveIssueCount += userRecord.SolveIssueCount | |||||
| resultMap[userRecord.ID].EncyclopediasCount += userRecord.EncyclopediasCount | |||||
| resultMap[userRecord.ID].CreateRepoCount += userRecord.CreateRepoCount | |||||
| resultMap[userRecord.ID].LoginCount += userRecord.LoginCount | |||||
| } | |||||
| } | |||||
| userBusinessAnalysisReturnList := make([]*UserBusinessAnalysis, len(resultMap)) | |||||
| index := 0 | |||||
| for _, v := range resultMap { | |||||
| userBusinessAnalysisReturnList[index] = v | |||||
| index += 1 | |||||
| } | |||||
| log.Info("return size=" + fmt.Sprint(len(userBusinessAnalysisReturnList))) | |||||
| return userBusinessAnalysisReturnList, count | |||||
| } | |||||
| func CounDataByDate(wikiCountMap map[string]int, startTime time.Time, endTime time.Time) { | func CounDataByDate(wikiCountMap map[string]int, startTime time.Time, endTime time.Time) { | ||||
| log.Info("start to count other user info data") | log.Info("start to count other user info data") | ||||
| sess := x.NewSession() | sess := x.NewSession() | ||||
| defer sess.Close() | defer sess.Close() | ||||
| sess.Select("`user`.*").Table("user") | |||||
| sess.Select("`user`.*").Table("user").Where("type != 1 and is_active=true") | |||||
| userList := make([]*User, 0) | userList := make([]*User, 0) | ||||
| sess.Find(&userList) | sess.Find(&userList) | ||||
| @@ -6,6 +6,7 @@ package storage | |||||
| import ( | import ( | ||||
| "io" | "io" | ||||
| "net/url" | |||||
| "path" | "path" | ||||
| "strconv" | "strconv" | ||||
| "strings" | "strings" | ||||
| @@ -262,6 +263,7 @@ func GetObsCreateSignedUrl(jobName, parentDir, fileName string) (string, error) | |||||
| input.Method = obs.HttpMethodGet | input.Method = obs.HttpMethodGet | ||||
| reqParams := make(map[string]string) | reqParams := make(map[string]string) | ||||
| fileName = url.QueryEscape(fileName) | |||||
| reqParams["response-content-disposition"] = "attachment; filename=\"" + fileName + "\"" | reqParams["response-content-disposition"] = "attachment; filename=\"" + fileName + "\"" | ||||
| input.QueryParams = reqParams | input.QueryParams = reqParams | ||||
| output, err := ObsCli.CreateSignedUrl(input) | output, err := ObsCli.CreateSignedUrl(input) | ||||
| @@ -5,6 +5,7 @@ import ( | |||||
| "fmt" | "fmt" | ||||
| "io/ioutil" | "io/ioutil" | ||||
| "net/http" | "net/http" | ||||
| "net/url" | |||||
| "os" | "os" | ||||
| "path" | "path" | ||||
| "strconv" | "strconv" | ||||
| @@ -107,6 +108,7 @@ func GetAllProjectsPeriodStatistics(ctx *context.Context) { | |||||
| ctx.Error(http.StatusBadRequest, ctx.Tr("repo.total_count_get_error")) | ctx.Error(http.StatusBadRequest, ctx.Tr("repo.total_count_get_error")) | ||||
| return | return | ||||
| } | } | ||||
| sql := generateSqlByType(ctx, beginTime, endTime, latestDate, q, orderBy, page, pageSize) | |||||
| projectsPeriodData := ProjectsPeriodData{ | projectsPeriodData := ProjectsPeriodData{ | ||||
| RecordBeginTime: recordBeginTime.Format(DATE_FORMAT), | RecordBeginTime: recordBeginTime.Format(DATE_FORMAT), | ||||
| @@ -114,13 +116,23 @@ func GetAllProjectsPeriodStatistics(ctx *context.Context) { | |||||
| TotalPage: getTotalPage(total, pageSize), | TotalPage: getTotalPage(total, pageSize), | ||||
| TotalCount: total, | TotalCount: total, | ||||
| LastUpdatedTime: latestUpdatedTime, | LastUpdatedTime: latestUpdatedTime, | ||||
| PageRecords: models.GetRepoStatisticByRawSql(generatePageSql(beginTime, endTime, latestDate, q, orderBy, page, pageSize)), | |||||
| PageRecords: models.GetRepoStatisticByRawSql(sql), | |||||
| } | } | ||||
| ctx.JSON(http.StatusOK, projectsPeriodData) | ctx.JSON(http.StatusOK, projectsPeriodData) | ||||
| } | } | ||||
| func generateSqlByType(ctx *context.Context, beginTime time.Time, endTime time.Time, latestDate string, q string, orderBy string, page int, pageSize int) string { | |||||
| sql := "" | |||||
| if ctx.QueryTrim("type") == "all" { | |||||
| sql = generateTypeAllSql(beginTime, endTime, latestDate, q, orderBy, page, pageSize) | |||||
| } else { | |||||
| sql = generatePageSql(beginTime, endTime, latestDate, q, orderBy, page, pageSize) | |||||
| } | |||||
| return sql | |||||
| } | |||||
| func ServeAllProjectsPeriodStatisticsFile(ctx *context.Context) { | func ServeAllProjectsPeriodStatisticsFile(ctx *context.Context) { | ||||
| recordBeginTime, err := getRecordBeginTime() | recordBeginTime, err := getRecordBeginTime() | ||||
| @@ -158,7 +170,7 @@ func ServeAllProjectsPeriodStatisticsFile(ctx *context.Context) { | |||||
| return | return | ||||
| } | } | ||||
| fileName := getFileName(ctx, beginTime, endTime) | |||||
| fileName, frontName := getFileName(ctx, beginTime, endTime) | |||||
| if err := os.MkdirAll(setting.RadarMap.Path, os.ModePerm); err != nil { | 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()) | ctx.Error(http.StatusBadRequest, fmt.Errorf("Failed to create dir %s: %v", setting.AvatarUploadPath, err).Error()) | ||||
| @@ -175,7 +187,7 @@ func ServeAllProjectsPeriodStatisticsFile(ctx *context.Context) { | |||||
| writer.Write(allProjectsPeroidHeader(ctx)) | writer.Write(allProjectsPeroidHeader(ctx)) | ||||
| for i := 0; i <= totalPage; i++ { | for i := 0; i <= totalPage; i++ { | ||||
| pageRecords := models.GetRepoStatisticByRawSql(generatePageSql(beginTime, endTime, latestDate, q, orderBy, i+1, pageSize)) | |||||
| pageRecords := models.GetRepoStatisticByRawSql(generateSqlByType(ctx, beginTime, endTime, latestDate, q, orderBy, i+1, pageSize)) | |||||
| for _, record := range pageRecords { | for _, record := range pageRecords { | ||||
| e = writer.Write(allProjectsPeroidValues(record, ctx)) | e = writer.Write(allProjectsPeroidValues(record, ctx)) | ||||
| if e != nil { | if e != nil { | ||||
| @@ -186,20 +198,24 @@ func ServeAllProjectsPeriodStatisticsFile(ctx *context.Context) { | |||||
| } | } | ||||
| ctx.ServeFile(fileName) | |||||
| ctx.ServeFile(fileName, url.QueryEscape(frontName)) | |||||
| } | } | ||||
| 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") + "_" | |||||
| } | |||||
| func getFileName(ctx *context.Context, beginTime time.Time, endTime time.Time) (string, string) { | |||||
| baseName := setting.RadarMap.Path + "/项目分析_" | |||||
| if ctx.QueryTrim("q") != "" { | if ctx.QueryTrim("q") != "" { | ||||
| baseName = baseName + ctx.QueryTrim("q") + "_" | baseName = baseName + ctx.QueryTrim("q") + "_" | ||||
| } | } | ||||
| baseName = baseName + beginTime.AddDate(0, 0, -1).Format(DATE_FORMAT) + "_to_" + endTime.AddDate(0, 0, -1).Format(DATE_FORMAT) + "_" + strconv.FormatInt(time.Now().Unix(), 10) + ".csv" | |||||
| return baseName | |||||
| if ctx.QueryTrim("type") == "all" { | |||||
| baseName = baseName + "所有" | |||||
| } else { | |||||
| baseName = baseName + beginTime.AddDate(0, 0, -1).Format(DATE_FORMAT) + "_" + endTime.AddDate(0, 0, -1).Format(DATE_FORMAT) | |||||
| } | |||||
| frontName := baseName + ".csv" | |||||
| localName := baseName + "_" + strconv.FormatInt(time.Now().Unix(), 10) + ".csv" | |||||
| return localName, path.Base(frontName) | |||||
| } | } | ||||
| func ClearUnusedStatisticsFile() { | func ClearUnusedStatisticsFile() { | ||||
| @@ -219,8 +235,8 @@ func ClearUnusedStatisticsFile() { | |||||
| func allProjectsPeroidHeader(ctx *context.Context) []string { | 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")} | |||||
| return []string{ctx.Tr("admin.repos.id"), ctx.Tr("admin.repos.projectName"), ctx.Tr("admin.repos.isPrivate"), ctx.Tr("admin.repos.openi"), ctx.Tr("admin.repos.visit"), ctx.Tr("admin.repos.download"), ctx.Tr("admin.repos.pr"), ctx.Tr("admin.repos.commit"), | |||||
| ctx.Tr("admin.repos.watches"), ctx.Tr("admin.repos.stars"), ctx.Tr("admin.repos.forks"), ctx.Tr("admin.repos.issues"), ctx.Tr("admin.repos.closedIssues"), ctx.Tr("admin.repos.contributor")} | |||||
| } | } | ||||
| @@ -234,9 +250,9 @@ func allProjectsPeroidValues(rs *models.RepoStatistic, ctx *context.Context) []s | |||||
| func getIsPrivateDisplay(private bool, ctx *context.Context) string { | func getIsPrivateDisplay(private bool, ctx *context.Context) string { | ||||
| if private { | if private { | ||||
| return ctx.Tr("repos.yes") | |||||
| return ctx.Tr("admin.repos.yes") | |||||
| } else { | } else { | ||||
| return ctx.Tr("repos.no") | |||||
| return ctx.Tr("admin.repos.no") | |||||
| } | } | ||||
| } | } | ||||
| @@ -358,11 +374,11 @@ func generateTargetSql(beginTime time.Time, endTime time.Time, repoId int64) str | |||||
| return sql | return sql | ||||
| } | } | ||||
| func generateCountSql(beginTime time.Time, endTime time.Time, yesterday string, q string) string { | |||||
| func generateCountSql(beginTime time.Time, endTime time.Time, latestDate string, q string) string { | |||||
| countSql := "SELECT count(*) FROM " + | countSql := "SELECT count(*) FROM " + | ||||
| "(SELECT repo_id FROM repo_statistic where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + | "(SELECT repo_id FROM repo_statistic where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + | ||||
| " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + " group by repo_id) A," + | " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + " group by repo_id) A," + | ||||
| "(SELECT repo_id,name,is_private,radar_total from public.repo_statistic where date='" + yesterday + "') B" + | |||||
| "(SELECT repo_id,name,is_private,radar_total from public.repo_statistic where date='" + latestDate + "') B" + | |||||
| " where A.repo_id=B.repo_id" | " where A.repo_id=B.repo_id" | ||||
| if q != "" { | if q != "" { | ||||
| countSql = countSql + " and B.name like '%" + q + "%'" | countSql = countSql + " and B.name like '%" + q + "%'" | ||||
| @@ -370,18 +386,34 @@ func generateCountSql(beginTime time.Time, endTime time.Time, yesterday string, | |||||
| return countSql | return countSql | ||||
| } | } | ||||
| func generatePageSql(beginTime time.Time, endTime time.Time, yesterday string, q string, orderBy string, page int, pageSize int) string { | |||||
| 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 " + | |||||
| func generateTypeAllSql(beginTime time.Time, endTime time.Time, latestDate string, q string, orderBy string, page int, pageSize int) string { | |||||
| sql := "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 " + | |||||
| "(SELECT repo_id,sum(num_visits) as num_visits " + | |||||
| " FROM repo_statistic where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + | |||||
| " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + " group by repo_id) A," + | |||||
| "(SELECT repo_id,name,is_private,radar_total,num_watches,num_downloads,num_pulls,num_commits,num_stars,num_forks,num_issues,num_closed_issues,num_contributor from public.repo_statistic where date='" + latestDate + "') B" + | |||||
| " where A.repo_id=B.repo_id" | |||||
| if q != "" { | |||||
| sql = sql + " and name like '%" + q + "%'" | |||||
| } | |||||
| sql = sql + " order by " + orderBy + " desc,repo_id" + " limit " + strconv.Itoa(pageSize) + " offset " + strconv.Itoa((page-1)*pageSize) | |||||
| return sql | |||||
| } | |||||
| func generatePageSql(beginTime time.Time, endTime time.Time, latestDate string, q string, orderBy string, page int, pageSize int) string { | |||||
| sql := "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 " + | |||||
| "(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 " + | "(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 " + | ||||
| " FROM repo_statistic where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + | " FROM repo_statistic where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + | ||||
| " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + " group by repo_id) A," + | " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + " group by repo_id) A," + | ||||
| "(SELECT repo_id,name,is_private,radar_total from public.repo_statistic where date='" + yesterday + "') B" + | |||||
| "(SELECT repo_id,name,is_private,radar_total from public.repo_statistic where date='" + latestDate + "') B" + | |||||
| " where A.repo_id=B.repo_id" | " where A.repo_id=B.repo_id" | ||||
| if q != "" { | if q != "" { | ||||
| countSql = countSql + " and B.name like '%" + q + "%'" | |||||
| sql = sql + " and B.name like '%" + q + "%'" | |||||
| } | } | ||||
| countSql = countSql + " order by " + orderBy + " desc,A.repo_id" + " limit " + strconv.Itoa(pageSize) + " offset " + strconv.Itoa((page-1)*pageSize) | |||||
| return countSql | |||||
| sql = sql + " order by " + orderBy + " desc,A.repo_id" + " limit " + strconv.Itoa(pageSize) + " offset " + strconv.Itoa((page-1)*pageSize) | |||||
| return sql | |||||
| } | } | ||||
| func getOrderBy(ctx *context.Context) string { | func getOrderBy(ctx *context.Context) string { | ||||
| @@ -9,6 +9,7 @@ import ( | |||||
| "code.gitea.io/gitea/modules/context" | "code.gitea.io/gitea/modules/context" | ||||
| "code.gitea.io/gitea/modules/git" | "code.gitea.io/gitea/modules/git" | ||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/setting" | |||||
| ) | ) | ||||
| func QueryUserStaticData(ctx *context.Context) { | func QueryUserStaticData(ctx *context.Context) { | ||||
| @@ -19,7 +20,36 @@ func QueryUserStaticData(ctx *context.Context) { | |||||
| endTime, _ := time.Parse("2006-01-02", endDate) | endTime, _ := time.Parse("2006-01-02", endDate) | ||||
| log.Info("startTime=" + fmt.Sprint(startTime.Unix()) + " endDate=" + fmt.Sprint(endTime.Unix())) | log.Info("startTime=" + fmt.Sprint(startTime.Unix()) + " endDate=" + fmt.Sprint(endTime.Unix())) | ||||
| ctx.JSON(http.StatusOK, models.QueryUserStaticData(startTime.Unix(), endTime.Unix())) | ctx.JSON(http.StatusOK, models.QueryUserStaticData(startTime.Unix(), endTime.Unix())) | ||||
| } | |||||
| func QueryUserStaticDataPage(ctx *context.Context) { | |||||
| startDate := ctx.Query("startDate") | |||||
| endDate := ctx.Query("endDate") | |||||
| page := ctx.QueryInt("page") | |||||
| if page <= 0 { | |||||
| page = 1 | |||||
| } | |||||
| userName := ctx.Query("userName") | |||||
| log.Info("startDate=" + startDate + " endDate=" + endDate + " userName=" + userName + " page=" + fmt.Sprint(page)) | |||||
| 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())) | |||||
| pageOpts := &models.UserBusinessAnalysisQueryOptions{ | |||||
| ListOptions: models.ListOptions{ | |||||
| Page: page, | |||||
| PageSize: setting.UI.IssuePagingNum, | |||||
| }, | |||||
| UserName: userName, | |||||
| StartTime: startTime.Unix(), | |||||
| EndTime: endTime.Unix(), | |||||
| } | |||||
| mapInterface := make(map[string]interface{}) | |||||
| re, count := models.QueryUserStaticDataPage(pageOpts) | |||||
| mapInterface["data"] = re | |||||
| mapInterface["count"] = count | |||||
| ctx.JSON(http.StatusOK, mapInterface) | |||||
| } | } | ||||
| func TimingCountDataByDate(date string) { | func TimingCountDataByDate(date string) { | ||||
| @@ -919,6 +919,7 @@ func Forks(ctx *context.Context) { | |||||
| } | } | ||||
| func Contributors(ctx *context.Context) { | func Contributors(ctx *context.Context) { | ||||
| ctx.Data["PageIsViewCode"] = true | |||||
| ctx.HTML(http.StatusOK, tplContributors) | ctx.HTML(http.StatusOK, tplContributors) | ||||
| } | } | ||||
| @@ -793,6 +793,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Post("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.UnitTypes(), repo.Action) | m.Post("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.UnitTypes(), repo.Action) | ||||
| m.Get("/tool/query_user_static", repo.QueryUserStaticData) | m.Get("/tool/query_user_static", repo.QueryUserStaticData) | ||||
| m.Get("/tool/query_user_static_page", repo.QueryUserStaticDataPage) | |||||
| // Grouping for those endpoints not requiring authentication | // Grouping for those endpoints not requiring authentication | ||||
| m.Group("/:username/:reponame", func() { | m.Group("/:username/:reponame", func() { | ||||
| m.Get("/contributors", repo.Contributors) | m.Get("/contributors", repo.Contributors) | ||||
| @@ -93,7 +93,7 @@ | |||||
| <div class="ui tabular stackable menu navbar"> | <div class="ui tabular stackable menu navbar"> | ||||
| {{if .Permission.CanRead $.UnitTypeCode}} | {{if .Permission.CanRead $.UnitTypeCode}} | ||||
| <div class="dropdown-menu"> | <div class="dropdown-menu"> | ||||
| <a class="{{if or .PageIsViewCode .PageIsReleaseList .PageIsWiki .PageIsActivity}}active{{end}} item" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}"> | |||||
| <a class="{{if or .PageIsViewCode .PageIsReleaseList .PageIsWiki .PageIsActivity .PageIsViewCode}}active{{end}} item" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}"> | |||||
| <span>{{svg "octicon-code" 16}} {{.i18n.Tr "repo.code"}} <i class="dropdown icon"></i></span> | <span>{{svg "octicon-code" 16}} {{.i18n.Tr "repo.code"}} <i class="dropdown icon"></i></span> | ||||
| </a> | </a> | ||||
| <div class="dropdown-content"> | <div class="dropdown-content"> | ||||