@@ -17,6 +17,7 @@ require ( | |||
gitea.com/macaron/macaron v1.4.0 | |||
gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d | |||
gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7 | |||
github.com/360EntSecGroup-Skylar/excelize v1.4.1 // indirect | |||
github.com/BurntSushi/toml v0.3.1 | |||
github.com/PuerkitoBio/goquery v1.5.0 | |||
github.com/RichardKnop/machinery v1.6.9 | |||
@@ -51,6 +51,8 @@ gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7 h1:N9QFoeNsUXLhl14m | |||
gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7/go.mod h1:kgsbFPPS4P+acDYDOPDa3N4IWWOuDJt5/INKRUz7aks= | |||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= | |||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= | |||
github.com/360EntSecGroup-Skylar/excelize v1.4.1 h1:l55mJb6rkkaUzOpSsgEeKYtS6/0gHwBYyfo5Jcjv/Ks= | |||
github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE= | |||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= | |||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | |||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= | |||
@@ -538,6 +540,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ | |||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= | |||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= | |||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= | |||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= | |||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= | |||
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c h1:3wkDRdxK92dF+c1ke2dtj7ZzemFWBHB9plnJOtlwdFA= | |||
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM= | |||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY= | |||
@@ -713,6 +717,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ | |||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= | |||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= | |||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | |||
github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | |||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | |||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= | |||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | |||
@@ -22,6 +22,7 @@ var customMigrations = []CustomMigration{ | |||
} | |||
var customMigrationsStatic = []CustomMigrationStatic{ | |||
{"Alter user static table field type open_i_index ", alterUserStaticTable}, | |||
{"Delete zuzhi user history data ", deleteNotDisplayUser}, | |||
} | |||
@@ -57,10 +58,17 @@ func syncTopicStruct(x *xorm.Engine) error { | |||
return err | |||
} | |||
func alterUserStaticTable(x *xorm.Engine, static *xorm.Engine) error { | |||
alterSql := "alter table public.user_business_analysis alter column open_i_index type double precision" | |||
_, err := static.Exec(alterSql) | |||
return err | |||
} | |||
func deleteNotDisplayUser(x *xorm.Engine, static *xorm.Engine) error { | |||
querySQL := "select id,name from public.user where type=1" | |||
rows, err := x.Query(querySQL) | |||
if err != nil { | |||
log.Info("select db failed,err:", err) | |||
@@ -68,8 +76,8 @@ func deleteNotDisplayUser(x *xorm.Engine, static *xorm.Engine) error { | |||
} | |||
for i, userRow := range rows { | |||
log.Info("delete zuzi user, i=" + fmt.Sprint(i) + " userName=" + string(userRow["id"])) | |||
deleteSql := "delete from user_business_analysis where id=" + string(userRow["id"]) + " and name='" + string(userRow["name"]) + "'" | |||
log.Info("delete zuzi user, i=" + fmt.Sprint(i) + " userName=" + string(userRow["name"])) | |||
deleteSql := "delete from public.user_business_analysis where id=" + string(userRow["id"]) + " and name='" + string(userRow["name"]) + "'" | |||
static.Exec(deleteSql) | |||
} | |||
@@ -3,6 +3,7 @@ package models | |||
import ( | |||
"encoding/json" | |||
"fmt" | |||
"sort" | |||
"time" | |||
"code.gitea.io/gitea/modules/log" | |||
@@ -65,13 +66,15 @@ type UserBusinessAnalysis struct { | |||
LoginCount int `xorm:"NOT NULL DEFAULT 0"` | |||
//openi index | |||
OpenIIndex int `xorm:"NOT NULL DEFAULT 0"` | |||
OpenIIndex float64 `xorm:"NOT NULL DEFAULT 0"` | |||
//user | |||
Email string `xorm:"NOT NULL"` | |||
//user | |||
Name string `xorm:"NOT NULL"` | |||
DataDate string `xorm:"NOT NULL"` | |||
} | |||
type UserBusinessAnalysisQueryOptions struct { | |||
@@ -82,6 +85,22 @@ type UserBusinessAnalysisQueryOptions struct { | |||
EndTime int64 | |||
} | |||
type UserBusinessAnalysisList []*UserBusinessAnalysis | |||
func (ulist UserBusinessAnalysisList) Swap(i, j int) { ulist[i], ulist[j] = ulist[j], ulist[i] } | |||
func (ulist UserBusinessAnalysisList) Len() int { return len(ulist) } | |||
func (ulist UserBusinessAnalysisList) Less(i, j int) bool { | |||
if ulist[i].CommitCount > ulist[j].CommitCount { | |||
return true | |||
} else { | |||
if ulist[i].CommitCount == ulist[j].CommitCount { | |||
return ulist[i].ID > ulist[j].ID | |||
} else { | |||
return false | |||
} | |||
} | |||
} | |||
func QueryUserStaticData(startTime int64, endTime int64) []*UserBusinessAnalysis { | |||
log.Info("query startTime =" + fmt.Sprint(startTime) + " endTime=" + fmt.Sprint(endTime)) | |||
statictisSess := xStatistic.NewSession() | |||
@@ -115,12 +134,11 @@ func QueryUserStaticData(startTime int64, endTime int64) []*UserBusinessAnalysis | |||
} | |||
} | |||
userBusinessAnalysisReturnList := make([]*UserBusinessAnalysis, len(resultMap)) | |||
index := 0 | |||
userBusinessAnalysisReturnList := UserBusinessAnalysisList{} | |||
for _, v := range resultMap { | |||
userBusinessAnalysisReturnList[index] = v | |||
index += 1 | |||
userBusinessAnalysisReturnList = append(userBusinessAnalysisReturnList, v) | |||
} | |||
sort.Sort(userBusinessAnalysisReturnList) | |||
log.Info("return size=" + fmt.Sprint(len(userBusinessAnalysisReturnList))) | |||
return userBusinessAnalysisReturnList | |||
} | |||
@@ -138,7 +156,7 @@ func QueryUserStaticDataPage(opts *UserBusinessAnalysisQueryOptions) ([]*UserBus | |||
var cond = builder.NewCond() | |||
if len(opts.UserName) > 0 { | |||
cond = cond.And( | |||
builder.Eq{"name": opts.UserName}, | |||
builder.Like{"name", opts.UserName}, | |||
) | |||
} | |||
cond = cond.And( | |||
@@ -218,17 +236,17 @@ func QueryUserStaticDataPage(opts *UserBusinessAnalysisQueryOptions) ([]*UserBus | |||
} | |||
} | |||
userBusinessAnalysisReturnList := make([]*UserBusinessAnalysis, len(resultMap)) | |||
index := 0 | |||
userBusinessAnalysisReturnList := UserBusinessAnalysisList{} | |||
for _, v := range resultMap { | |||
userBusinessAnalysisReturnList[index] = v | |||
index += 1 | |||
userBusinessAnalysisReturnList = append(userBusinessAnalysisReturnList, v) | |||
} | |||
sort.Sort(userBusinessAnalysisReturnList) | |||
log.Info("return size=" + fmt.Sprint(len(userBusinessAnalysisReturnList))) | |||
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() | |||
@@ -246,9 +264,12 @@ 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, startTime.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) | |||
@@ -285,6 +306,7 @@ func CounDataByDate(wikiCountMap map[string]int, startTime time.Time, endTime ti | |||
dateRecord.RegistDate = userRecord.CreatedUnix | |||
dateRecord.Name = userRecord.Name | |||
dateRecord.GiteaAgeMonth = subMonth(currentTimeNow, userRecord.CreatedUnix.AsTime()) | |||
dateRecord.DataDate = DataDate | |||
if _, ok := CodeMergeCountMap[dateRecord.ID]; !ok { | |||
dateRecord.CodeMergeCount = 0 | |||
} else { | |||
@@ -366,7 +388,7 @@ func CounDataByDate(wikiCountMap map[string]int, startTime time.Time, endTime ti | |||
if _, ok := OpenIIndexMap[dateRecord.ID]; !ok { | |||
dateRecord.OpenIIndex = 0 | |||
} else { | |||
dateRecord.OpenIIndex = int(OpenIIndexMap[dateRecord.ID] * 100) | |||
dateRecord.OpenIIndex = OpenIIndexMap[dateRecord.ID] | |||
} | |||
dateRecord.CommitModelCount = 0 | |||
@@ -376,6 +398,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() | |||
@@ -507,10 +533,10 @@ func queryFollow(start_unix int64, end_unix int64) map[int64]int { | |||
resultMap := make(map[int64]int) | |||
log.Info("query Follow size=" + fmt.Sprint(len(followList))) | |||
for _, followRecord := range followList { | |||
if _, ok := resultMap[followRecord.UserID]; !ok { | |||
resultMap[followRecord.UserID] = 1 | |||
if _, ok := resultMap[followRecord.FollowID]; !ok { | |||
resultMap[followRecord.FollowID] = 1 | |||
} else { | |||
resultMap[followRecord.UserID] += 1 | |||
resultMap[followRecord.FollowID] += 1 | |||
} | |||
} | |||
return resultMap | |||
@@ -10,6 +10,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" | |||
) | |||
func QueryUserStaticData(ctx *context.Context) { | |||
@@ -18,6 +19,7 @@ func QueryUserStaticData(ctx *context.Context) { | |||
log.Info("startDate=" + startDate + " endDate=" + endDate) | |||
startTime, _ := time.Parse("2006-01-02", startDate) | |||
endTime, _ := time.Parse("2006-01-02", endDate) | |||
endTime = endTime.AddDate(0, 0, 1) | |||
log.Info("startTime=" + fmt.Sprint(startTime.Unix()) + " endDate=" + fmt.Sprint(endTime.Unix())) | |||
ctx.JSON(http.StatusOK, models.QueryUserStaticData(startTime.Unix(), endTime.Unix())) | |||
} | |||
@@ -29,17 +31,24 @@ func QueryUserStaticDataPage(ctx *context.Context) { | |||
if page <= 0 { | |||
page = 1 | |||
} | |||
pageSize := ctx.QueryInt("pageSize") | |||
if pageSize <= 0 { | |||
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) | |||
endTime, _ := time.Parse("2006-01-02", endDate) | |||
endTime = endTime.AddDate(0, 0, 1) | |||
log.Info("startTime=" + fmt.Sprint(startTime.Unix()) + " endDate=" + fmt.Sprint(endTime.Unix())) | |||
pageOpts := &models.UserBusinessAnalysisQueryOptions{ | |||
ListOptions: models.ListOptions{ | |||
Page: page, | |||
PageSize: setting.UI.IssuePagingNum, | |||
PageSize: pageSize, | |||
}, | |||
UserName: userName, | |||
StartTime: startTime.Unix(), | |||
@@ -49,11 +58,71 @@ func QueryUserStaticDataPage(ctx *context.Context) { | |||
re, count := models.QueryUserStaticDataPage(pageOpts) | |||
mapInterface["data"] = re | |||
mapInterface["count"] = count | |||
ctx.JSON(http.StatusOK, mapInterface) | |||
} | |||
func TimingCountDataByDate(date string) { | |||
if IsReturnFile { | |||
//writer exec file. | |||
xlsx := excelize.NewFile() | |||
sheetName := "用户分析" | |||
index := xlsx.NewSheet(sheetName) | |||
dataHeader := map[string]string{ | |||
//学科 | |||
"A1": "ID", | |||
"B1": "用户名", | |||
"C1": "PR数", | |||
"D1": "提出任务数", | |||
"E1": "评论数", | |||
"F1": "关注项目数", | |||
"G1": "点赞项目数", | |||
"H1": "登录次数", | |||
"I1": "关注者数", | |||
"J1": "commit代码行数", | |||
"K1": "已解决任务数", | |||
"L1": "百科页面贡献次数", | |||
"M1": "创建项目", | |||
"N1": "OpenI指数", | |||
"O1": "用户注册时间", | |||
"P1": "系统统计时间", | |||
} | |||
for k, v := range dataHeader { | |||
//设置单元格的值 | |||
xlsx.SetCellValue(sheetName, k, v) | |||
} | |||
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.IssueCount) | |||
xlsx.SetCellValue(sheetName, "E"+rows, userRecord.CommentCount) | |||
xlsx.SetCellValue(sheetName, "F"+rows, userRecord.FocusRepoCount) | |||
xlsx.SetCellValue(sheetName, "G"+rows, userRecord.StarRepoCount) | |||
xlsx.SetCellValue(sheetName, "H"+rows, userRecord.LoginCount) | |||
xlsx.SetCellValue(sheetName, "I"+rows, userRecord.WatchedCount) | |||
xlsx.SetCellValue(sheetName, "J"+rows, userRecord.CommitCodeSize) | |||
xlsx.SetCellValue(sheetName, "K"+rows, userRecord.SolveIssueCount) | |||
xlsx.SetCellValue(sheetName, "L"+rows, userRecord.EncyclopediasCount) | |||
xlsx.SetCellValue(sheetName, "M"+rows, userRecord.CreateRepoCount) | |||
xlsx.SetCellValue(sheetName, "N"+rows, userRecord.OpenIIndex) | |||
xlsx.SetCellValue(sheetName, "O"+rows, userRecord.RegistDate.Format("2006-01-02")) | |||
xlsx.SetCellValue(sheetName, "P"+rows, time.Unix(userRecord.CountDate, 0).Format("2006-01-02")) | |||
} | |||
//设置默认打开的表单 | |||
xlsx.SetActiveSheet(index) | |||
err := xlsx.SaveAs("./成绩表.xlsx") | |||
if err != nil { | |||
log.Error(err) | |||
} | |||
} 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()) | |||
@@ -100,16 +169,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) | |||
} |
@@ -792,8 +792,8 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
}, reqSignIn, context.RepoAssignment(), context.UnitTypes(), reqRepoAdmin, context.RepoRef()) | |||
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_page", repo.QueryUserStaticDataPage) | |||
m.Get("/tool/query_user_static", adminReq, repo.QueryUserStaticData) | |||
m.Get("/tool/query_user_static_page", adminReq, repo.QueryUserStaticDataPage) | |||
// Grouping for those endpoints not requiring authentication | |||
m.Group("/:username/:reponame", func() { | |||
m.Get("/contributors", repo.Contributors) | |||