diff --git a/models/user_business_analysis.go b/models/user_business_analysis.go index 00d8e3f8a..1223224ee 100644 --- a/models/user_business_analysis.go +++ b/models/user_business_analysis.go @@ -7,7 +7,6 @@ import ( "time" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" "xorm.io/builder" ) @@ -23,7 +22,7 @@ type UserBusinessAnalysis struct { //action :ActionCommitRepo // 5 CommitCount int `xorm:"NOT NULL DEFAULT 0"` - //action :ActionCommentIssue // 10 + //action :ActionCreateIssue // 10 IssueCount int `xorm:"NOT NULL DEFAULT 0"` //comment table current date @@ -74,7 +73,7 @@ type UserBusinessAnalysis struct { //user Name string `xorm:"NOT NULL"` - DataDate string `xorm:"NULL"` + DataDate string `xorm:"NOT NULL"` } type UserBusinessAnalysisQueryOptions struct { @@ -160,10 +159,10 @@ func QueryUserStaticDataPage(opts *UserBusinessAnalysisQueryOptions) ([]*UserBus ) } cond = cond.And( - builder.Gte{"count_date": fmt.Sprint(pageStartTime)}, + builder.Gte{"count_date": pageStartTime.Unix()}, ) cond = cond.And( - builder.Lte{"count_date": fmt.Sprint(pageEndTime)}, + builder.Lte{"count_date": pageEndTime.Unix()}, ) count, err := statictisSess.Where(cond).Count(new(UserBusinessAnalysis)) @@ -183,7 +182,7 @@ func QueryUserStaticDataPage(opts *UserBusinessAnalysisQueryOptions) ([]*UserBus } statictisSess.OrderBy("count_date desc") - userBusinessAnalysisList := make([]*UserBusinessAnalysis, 0, setting.UI.IssuePagingNum) + userBusinessAnalysisList := make([]*UserBusinessAnalysis, 0) if err := statictisSess.Table("user_business_analysis").Where(cond). Find(&userBusinessAnalysisList); err != nil { return nil, 0 @@ -191,27 +190,29 @@ func QueryUserStaticDataPage(opts *UserBusinessAnalysisQueryOptions) ([]*UserBus resultMap := make(map[int64]*UserBusinessAnalysis) - var newAndCond = builder.NewCond() - var newOrCond = builder.NewCond() - for _, userRecord := range userBusinessAnalysisList { - newOrCond.Or( - builder.Eq{"id": userRecord.ID}, + if opts.Page >= 0 && opts.PageSize > 0 { + var newAndCond = builder.NewCond() + var newOrCond = builder.NewCond() + for _, userRecord := range userBusinessAnalysisList { + newOrCond = newOrCond.Or( + builder.Eq{"id": userRecord.ID}, + ) + } + newAndCond = newAndCond.And( + newOrCond, + ) + newAndCond = newAndCond.And( + builder.Gte{"count_date": opts.StartTime}, + ) + newAndCond = newAndCond.And( + builder.Lte{"count_date": opts.EndTime}, ) - } - 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 + 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))) @@ -245,7 +246,8 @@ func QueryUserStaticDataPage(opts *UserBusinessAnalysisQueryOptions) ([]*UserBus return userBusinessAnalysisReturnList, count } -func CounDataByDate(wikiCountMap map[string]int, startTime time.Time, endTime time.Time) { +func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time, endTime time.Time, isReCount bool) { + log.Info("start to count other user info data") sess := x.NewSession() defer sess.Close() @@ -263,12 +265,15 @@ func CounDataByDate(wikiCountMap map[string]int, startTime time.Time, endTime ti //endTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, currentTimeNow.Location()) end_unix := endTime.Unix() - CountDate := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 1, 0, 0, currentTimeNow.Location()) + if isReCount { + CountDate = time.Date(startTime.Year(), startTime.Month(), startTime.Day(), 0, 1, 0, 0, currentTimeNow.Location()) + } + DataDate := startTime.Format("2006-01-02") CodeMergeCountMap := queryPullRequest(start_unix, end_unix) CommitCountMap := queryAction(start_unix, end_unix, 5) - IssueCountMap := queryAction(start_unix, end_unix, 10) + IssueCountMap := queryAction(start_unix, end_unix, 6) CommentCountMap := queryComment(start_unix, end_unix) FocusRepoCountMap := queryWatch(start_unix, end_unix) @@ -394,6 +399,10 @@ func CounDataByDate(wikiCountMap map[string]int, startTime time.Time, endTime ti } +func CounDataByDate(wikiCountMap map[string]int, startTime time.Time, endTime time.Time) { + CounDataByDateAndReCount(wikiCountMap, startTime, endTime, false) +} + func querySolveIssue(start_unix int64, end_unix int64) map[int64]int { //select issue_assignees.* from issue_assignees,issue where issue.is_closed=true and issue.id=issue_assignees.issue_id sess := x.NewSession() @@ -665,5 +674,8 @@ func subMonth(t1, t2 time.Time) (month int) { } monthInterval %= 12 month = yearInterval*12 + monthInterval + if month == 0 { + month = 1 + } return month } diff --git a/modules/cron/tasks_basic.go b/modules/cron/tasks_basic.go index e238418d8..207018c20 100755 --- a/modules/cron/tasks_basic.go +++ b/modules/cron/tasks_basic.go @@ -11,7 +11,6 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/migrations" repository_service "code.gitea.io/gitea/modules/repository" - api_repo "code.gitea.io/gitea/routers/api/v1/repo" "code.gitea.io/gitea/routers/repo" mirror_service "code.gitea.io/gitea/services/mirror" ) @@ -186,17 +185,6 @@ func registerHandleSummaryStatistic() { }) } -func registerHandleClearRepoStatisticFile() { - RegisterTaskFatal("handle_repo_clear_statistic_file", &BaseConfig{ - Enabled: true, - RunAtStart: false, - Schedule: "@daily", - }, func(ctx context.Context, _ *models.User, _ Config) error { - api_repo.ClearUnusedStatisticsFile() - return nil - }) -} - func initBasicTasks() { registerUpdateMirrorTask() registerRepoHealthCheck() diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 9a4cd191b..cac2153bb 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -402,6 +402,26 @@ form.name_reserved = The username '%s' is reserved. form.name_pattern_not_allowed = The pattern '%s' is not allowed in a username. form.name_chars_not_allowed = User name '%s' contains invalid characters. +static.sheetname=User Analysis +static.id=ID +static.name=User Name +static.codemergecount=PR Count +static.commitcount=Commit Count +static.issuecount=Issue Count +static.commentcount=Comment Count +static.focusrepocount=Focus Repo Count +static.starrepocount=Repo Star Count +static.logincount=Login Count +static.watchedcount=Watched Count +static.commitcodesize=Commit Code Line +static.solveissuecount=Solve Issue Count +static.encyclopediascount=Encyclopedias Count +static.createrepocount=Create Repo Count +static.openiindex=OpenI Index +static.registdate=Regist Date +static.countdate=Count Date + + [settings] profile = Profile account = Account diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index d37bd61c7..9918d47e0 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -405,6 +405,24 @@ form.name_reserved='%s' 用户名被保留。 form.name_pattern_not_allowed=用户名中不允许使用 "%s"。 form.name_chars_not_allowed=用户名 '%s' 包含无效字符。 +static.sheetname=用户分析 +static.id=ID +static.name=用户名 +static.codemergecount=PR数 +static.commitcount=commit次数 +static.issuecount=提出任务数 +static.commentcount=评论数 +static.focusrepocount=关注项目数 +static.starrepocount=点赞项目数 +static.logincount=登录次数 +static.watchedcount=关注者数 +static.commitcodesize=commit代码行数 +static.solveissuecount=已解决任务数 +static.encyclopediascount=百科页面贡献次数 +static.createrepocount=创建项目数 +static.openiindex=OpenI指数 +static.registdate=用户注册时间 +static.countdate=系统统计时间 [settings] profile=个人信息 account=账号 diff --git a/routers/api/v1/repo/repo_dashbord.go b/routers/api/v1/repo/repo_dashbord.go index 2becdd59f..041162039 100644 --- a/routers/api/v1/repo/repo_dashbord.go +++ b/routers/api/v1/repo/repo_dashbord.go @@ -2,7 +2,6 @@ package repo import ( "fmt" - "io/ioutil" "net/http" "net/url" "os" @@ -174,7 +173,7 @@ func ServeAllProjectsPeriodStatisticsFile(ctx *context.Context) { return } var projectAnalysis = ctx.Tr("repo.repo_stat_inspect") - fileName, frontName := getFileName(ctx, beginTime, endTime, projectAnalysis) + fileName := getFileName(ctx, beginTime, endTime, projectAnalysis) 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()) @@ -207,17 +206,14 @@ func ServeAllProjectsPeriodStatisticsFile(ctx *context.Context) { f.SetActiveSheet(index) f.DeleteSheet("Sheet1") - if err := f.SaveAs(fileName); err != nil { - log.Error("Can not generate file.", err) - ctx.Error(http.StatusBadRequest, ctx.Tr("repo.generate_statistic_file_error")) - return - } + ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+url.QueryEscape(fileName)) + ctx.Resp.Header().Set("Content-Type", "application/octet-stream") - ctx.ServeFile(fileName, url.QueryEscape(frontName)) + f.WriteTo(ctx.Resp) } -func getFileName(ctx *context.Context, beginTime time.Time, endTime time.Time, projectAnalysis string) (string, string) { +func getFileName(ctx *context.Context, beginTime time.Time, endTime time.Time, projectAnalysis string) string { baseName := setting.RadarMap.Path + "/" + projectAnalysis + "_" if ctx.QueryTrim("q") != "" { @@ -229,23 +225,7 @@ func getFileName(ctx *context.Context, beginTime time.Time, endTime time.Time, p baseName = baseName + beginTime.AddDate(0, 0, -1).Format(EXCEL_DATE_FORMAT) + "_" + endTime.AddDate(0, 0, -1).Format(EXCEL_DATE_FORMAT) } frontName := baseName + ".xlsx" - localName := baseName + "_" + strconv.FormatInt(time.Now().Unix(), 10) + ".xlsx" - return localName, path.Base(frontName) -} - -func ClearUnusedStatisticsFile() { - fileInfos, err := ioutil.ReadDir(setting.RadarMap.Path) - if err != nil { - log.Warn("can not read dir: "+setting.RadarMap.Path, err) - return - } - - for _, fileInfo := range fileInfos { - if !fileInfo.IsDir() && fileInfo.ModTime().Before(time.Now().AddDate(0, 0, -1)) { - os.Remove(path.Join(setting.RadarMap.Path, fileInfo.Name())) - } - } - + return path.Base(frontName) } func allProjectsPeroidHeader(ctx *context.Context) map[string]string { diff --git a/routers/repo/user_data_analysis.go b/routers/repo/user_data_analysis.go index 3742cf67c..134896177 100755 --- a/routers/repo/user_data_analysis.go +++ b/routers/repo/user_data_analysis.go @@ -3,6 +3,8 @@ package repo import ( "fmt" "net/http" + "net/url" + "strings" "time" "code.gitea.io/gitea/models" @@ -10,6 +12,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "github.com/360EntSecGroup-Skylar/excelize/v2" ) func QueryUserStaticData(ctx *context.Context) { @@ -35,6 +38,7 @@ func QueryUserStaticDataPage(ctx *context.Context) { pageSize = setting.UI.IssuePagingNum } userName := ctx.Query("userName") + IsReturnFile := ctx.QueryBool("IsReturnFile") log.Info("startDate=" + startDate + " endDate=" + endDate + " userName=" + userName + " page=" + fmt.Sprint(page)) startTime, _ := time.Parse("2006-01-02", startDate) @@ -42,6 +46,11 @@ func QueryUserStaticDataPage(ctx *context.Context) { endTime = endTime.AddDate(0, 0, 1) log.Info("startTime=" + fmt.Sprint(startTime.Unix()) + " endDate=" + fmt.Sprint(endTime.Unix())) + if IsReturnFile { + page = -1 + pageSize = -1 + } + pageOpts := &models.UserBusinessAnalysisQueryOptions{ ListOptions: models.ListOptions{ Page: page, @@ -55,11 +64,76 @@ func QueryUserStaticDataPage(ctx *context.Context) { re, count := models.QueryUserStaticDataPage(pageOpts) mapInterface["data"] = re mapInterface["count"] = count - ctx.JSON(http.StatusOK, mapInterface) -} + if IsReturnFile { + //writer exec file. + xlsx := excelize.NewFile() + sheetName := ctx.Tr("user.static.sheetname") + index := xlsx.NewSheet(sheetName) + dataHeader := map[string]string{ + "A1": ctx.Tr("user.static.id"), + "B1": ctx.Tr("user.static.name"), + "C1": ctx.Tr("user.static.codemergecount"), + "D1": ctx.Tr("user.static.commitcount"), + "E1": ctx.Tr("user.static.issuecount"), + "F1": ctx.Tr("user.static.commentcount"), + "G1": ctx.Tr("user.static.focusrepocount"), + "H1": ctx.Tr("user.static.starrepocount"), + "I1": ctx.Tr("user.static.logincount"), + "J1": ctx.Tr("user.static.watchedcount"), + "K1": ctx.Tr("user.static.commitcodesize"), + "L1": ctx.Tr("user.static.solveissuecount"), + "M1": ctx.Tr("user.static.encyclopediascount"), + "N1": ctx.Tr("user.static.createrepocount"), + "O1": ctx.Tr("user.static.openiindex"), + "P1": ctx.Tr("user.static.registdate"), + "Q1": ctx.Tr("user.static.countdate"), + } + for k, v := range dataHeader { + //设置单元格的值 + xlsx.SetCellValue(sheetName, k, v) + } -func TimingCountDataByDate(date string) { + for i, userRecord := range re { + rows := fmt.Sprint(i + 2) + + xlsx.SetCellValue(sheetName, "A"+rows, userRecord.ID) + xlsx.SetCellValue(sheetName, "B"+rows, userRecord.Name) + xlsx.SetCellValue(sheetName, "C"+rows, userRecord.CodeMergeCount) + xlsx.SetCellValue(sheetName, "D"+rows, userRecord.CommitCount) + xlsx.SetCellValue(sheetName, "E"+rows, userRecord.IssueCount) + xlsx.SetCellValue(sheetName, "F"+rows, userRecord.CommentCount) + xlsx.SetCellValue(sheetName, "G"+rows, userRecord.FocusRepoCount) + xlsx.SetCellValue(sheetName, "H"+rows, userRecord.StarRepoCount) + xlsx.SetCellValue(sheetName, "I"+rows, userRecord.LoginCount) + xlsx.SetCellValue(sheetName, "J"+rows, userRecord.WatchedCount) + xlsx.SetCellValue(sheetName, "K"+rows, userRecord.CommitCodeSize) + xlsx.SetCellValue(sheetName, "L"+rows, userRecord.SolveIssueCount) + xlsx.SetCellValue(sheetName, "M"+rows, userRecord.EncyclopediasCount) + xlsx.SetCellValue(sheetName, "N"+rows, userRecord.CreateRepoCount) + xlsx.SetCellValue(sheetName, "O"+rows, userRecord.OpenIIndex) + xlsx.SetCellValue(sheetName, "P"+rows, userRecord.RegistDate.Format("2006-01-02")) + xlsx.SetCellValue(sheetName, "Q"+rows, time.Unix(userRecord.CountDate, 0).Format("2006-01-02")) + } + + //设置默认打开的表单 + xlsx.SetActiveSheet(index) + filename := sheetName + "_" + strings.ReplaceAll(startDate, "-", "") + "_" + strings.ReplaceAll(endDate, "-", "") + ".xlsx" + if len(userName) > 0 { + filename = sheetName + "_" + userName + "_" + strings.ReplaceAll(startDate, "-", "") + "_" + strings.ReplaceAll(endDate, "-", "") + ".xlsx" + } + ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+url.QueryEscape(filename)) + ctx.Resp.Header().Set("Content-Type", "application/octet-stream") + if _, err := xlsx.WriteTo(ctx.Resp); err != nil { + log.Info("writer exel error." + err.Error()) + } + + } else { + ctx.JSON(http.StatusOK, mapInterface) + } +} + +func TimingCountDataByDateAndReCount(date string, isReCount bool) { t, _ := time.Parse("2006-01-02", date) startTime := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) @@ -106,16 +180,17 @@ func TimingCountDataByDate(date string) { } } //other user info data - models.CounDataByDate(wikiMap, startTime, endTime) + models.CounDataByDateAndReCount(wikiMap, startTime, endTime, isReCount) +} +func TimingCountDataByDate(date string) { + TimingCountDataByDateAndReCount(date, true) } func TimingCountData() { - log.Info("start to time count data") currentTimeNow := time.Now() log.Info("current time:" + currentTimeNow.Format("2006-01-02 15:04:05")) startTime := currentTimeNow.AddDate(0, 0, -1).Format("2006-01-02") - - TimingCountDataByDate(startTime) + TimingCountDataByDateAndReCount(startTime, false) }