From b18906605a17b488276293bd8f9bf803bdf25e50 Mon Sep 17 00:00:00 2001 From: yystopf Date: Tue, 20 Dec 2022 18:16:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E6=8F=90=E4=BA=A4=E6=AC=A1=E6=95=B0=E5=88=86=E5=B8=83api?= =?UTF-8?q?=EF=BC=88=E6=8C=89=E6=97=B6=E9=97=B4=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- models/action.go | 27 ------- models/activities/action.go | 114 ++++++++++++++++++++++++++++++ models/activities/user_heatmap.go | 55 ++++++++++++++ routers/hat/hat.go | 10 +++ routers/hat/repo/repo.go | 4 +- routers/hat/user/user.go | 27 +++++++ 6 files changed, 208 insertions(+), 29 deletions(-) delete mode 100644 models/action.go create mode 100644 models/activities/action.go create mode 100644 models/activities/user_heatmap.go create mode 100644 routers/hat/user/user.go diff --git a/models/action.go b/models/action.go deleted file mode 100644 index 513cb4b..0000000 --- a/models/action.go +++ /dev/null @@ -1,27 +0,0 @@ -package models - -import ( - "code.gitea.io/gitea/models/db" -) - -type Contributor struct { - ID int64 `json:"id"` - Name string `json:"name"` - Email string `json:"email"` - Contributions int64 `json:"contributions"` -} - -func GetContributors(repoID, userID int64) (result []Contributor, err error) { - sql := - `select a.act_user_id as id , - u.name as login, - u.email as email, - count(act_user_id) as contributions - from action a - left join user u on a.act_user_id=u.id - where repo_id=? and user_id=? - group by repo_id,act_user_id ` - - err = db.GetEngine(db.DefaultContext).SQL(sql, repoID, userID).Find(&result) - return -} diff --git a/models/activities/action.go b/models/activities/action.go new file mode 100644 index 0000000..e64271d --- /dev/null +++ b/models/activities/action.go @@ -0,0 +1,114 @@ +package activities + +import ( + "fmt" + "time" + + gitea_activities_models "code.gitea.io/gitea/models/activities" + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/organization" + gitea_repo_model "code.gitea.io/gitea/models/repo" + gitea_user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" + + "xorm.io/builder" +) + +type Contributor struct { + ID int64 `json:"id"` + Name string `json:"name"` + Email string `json:"email"` + Contributions int64 `json:"contributions"` +} + +func GetContributors(repoID, userID int64) (result []Contributor, err error) { + sql := + `select a.act_user_id as id , + u.name as login, + u.email as email, + count(act_user_id) as contributions + from action a + left join user u on a.act_user_id=u.id + where repo_id=? and user_id=? + group by repo_id,act_user_id ` + + err = db.GetEngine(db.DefaultContext).SQL(sql, repoID, userID).Find(&result) + return +} + +func activityQueryCondition(opts gitea_activities_models.GetFeedsOptions) (builder.Cond, error) { + cond := builder.NewCond() + + if opts.RequestedTeam != nil && opts.RequestedUser == nil { + org, err := gitea_user_model.GetUserByID(opts.RequestedTeam.OrgID) + if err != nil { + return nil, err + } + opts.RequestedUser = org + } + + // check activity visibility for actor ( similar to activityReadable() ) + if opts.Actor == nil { + cond = cond.And(builder.In("act_user_id", + builder.Select("`user`.id").Where( + builder.Eq{"keep_activity_private": false, "visibility": structs.VisibleTypePublic}, + ).From("`user`"), + )) + } else if !opts.Actor.IsAdmin { + cond = cond.And(builder.In("act_user_id", + builder.Select("`user`.id").Where( + builder.Eq{"keep_activity_private": false}. + And(builder.In("visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited))). + Or(builder.Eq{"id": opts.Actor.ID}).From("`user`"), + )) + } + + // check readable repositories by doer/actor + if opts.Actor == nil || !opts.Actor.IsAdmin { + cond = cond.And(builder.In("repo_id", gitea_repo_model.AccessibleRepoIDsQuery(opts.Actor))) + } + + if opts.RequestedRepo != nil { + cond = cond.And(builder.Eq{"repo_id": opts.RequestedRepo.ID}) + } + + if opts.RequestedTeam != nil { + env := organization.OrgFromUser(opts.RequestedUser).AccessibleTeamReposEnv(opts.RequestedTeam) + teamRepoIDs, err := env.RepoIDs(1, opts.RequestedUser.NumRepos) + if err != nil { + return nil, fmt.Errorf("GetTeamRepositories: %w", err) + } + cond = cond.And(builder.In("repo_id", teamRepoIDs)) + } + + if opts.RequestedUser != nil { + cond = cond.And(builder.Eq{"user_id": opts.RequestedUser.ID}) + + if opts.OnlyPerformedBy { + cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID}) + } + } + + if !opts.IncludePrivate { + cond = cond.And(builder.Eq{"`action`.is_private": false}) + } + if !opts.IncludeDeleted { + cond = cond.And(builder.Eq{"is_deleted": false}) + } + + if opts.Date != "" { + dateLow, err := time.ParseInLocation("2006-01-02", opts.Date, setting.DefaultUILocation) + if err != nil { + log.Warn("Unable to parse %s, filter not applied: %v", opts.Date, err) + } else { + dateHigh := dateLow.Add(86399000000000) // 23h59m59s + + cond = cond.And(builder.Gte{"`action`.created_unix": dateLow.Unix()}) + cond = cond.And(builder.Lte{"`action`.created_unix": dateHigh.Unix()}) + } + } + + return cond, nil +} diff --git a/models/activities/user_heatmap.go b/models/activities/user_heatmap.go new file mode 100644 index 0000000..22c4605 --- /dev/null +++ b/models/activities/user_heatmap.go @@ -0,0 +1,55 @@ +package activities + +import ( + gitea_activities_models "code.gitea.io/gitea/models/activities" + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/organization" + gitea_user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/timeutil" +) + +type UserHeatmapData struct { + Timestamp timeutil.TimeStamp `json:"timestamp"` + Contributions int64 `json:"contributions"` +} + +func GetUserHeatmapDataByTimestampRange(user *gitea_user_model.User, team *organization.Team, doer *gitea_user_model.User, startAt, endAt timeutil.TimeStamp) ([]*UserHeatmapData, error) { + hdata := make([]*UserHeatmapData, 0) + if !gitea_activities_models.ActivityReadable(user, doer) { + return hdata, nil + } + + groupBy := "created_unix / 900 * 900" + groupByName := "timestamp" + + switch { + case setting.Database.UseMySQL: + groupBy = "created_unix DIV 900 * 900" + case setting.Database.UseMSSQL: + groupByName = groupBy + } + + cond, err := activityQueryCondition(gitea_activities_models.GetFeedsOptions{ + RequestedUser: user, + RequestedTeam: team, + Actor: doer, + IncludePrivate: true, + IncludeDeleted: true, + OnlyPerformedBy: !user.IsOrganization(), + }) + + if err != nil { + return nil, err + } + + return hdata, db.GetEngine(db.DefaultContext). + Select(groupBy+" AS timestamp, count(user_id) as contributions"). + Table("action"). + Where(cond). + And("created_unix >= ?", startAt). + And("created_unix <= ?", endAt). + GroupBy(groupByName). + OrderBy("timestamp"). + Find(&hdata) +} diff --git a/routers/hat/hat.go b/routers/hat/hat.go index b239f30..899e322 100644 --- a/routers/hat/hat.go +++ b/routers/hat/hat.go @@ -22,8 +22,10 @@ import ( "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/misc" "code.gitea.io/gitea/services/auth" + context_service "code.gitea.io/gitea/services/context" "code.gitlink.org.cn/Gitlink/gitea_hat.git/routers/hat/org" "code.gitlink.org.cn/Gitlink/gitea_hat.git/routers/hat/repo" + "code.gitlink.org.cn/Gitlink/gitea_hat.git/routers/hat/user" "github.com/go-chi/cors" ) @@ -106,6 +108,14 @@ func Routers(ctx gocontext.Context) *web.Route { }) }, repoAssignment()) }) + m.Group("/users", func() { + + m.Group("/{username}", func() { + if setting.Service.EnableUserHeatmap { + m.Get("/heatmap", user.GetUserHeatmapData) + } + }, context_service.UserAssignmentAPI()) + }) m.Post("/orgs", reqToken(), bind(gitea_api.CreateOrgOption{}), org.Create) }) diff --git a/routers/hat/repo/repo.go b/routers/hat/repo/repo.go index 097dcf4..9e82d44 100644 --- a/routers/hat/repo/repo.go +++ b/routers/hat/repo/repo.go @@ -16,7 +16,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/services/gitdiff" - hat_models "code.gitlink.org.cn/Gitlink/gitea_hat.git/models" + hat_activities_models "code.gitlink.org.cn/Gitlink/gitea_hat.git/models/activities" ) func PrepareComapreDiff( @@ -481,7 +481,7 @@ func SetDiffViewStyle(ctx *context.Context) { } func GetContributors(ctx *context.APIContext) { - list, err := hat_models.GetContributors(ctx.Repo.Repository.ID, ctx.Repo.Owner.ID) + list, err := hat_activities_models.GetContributors(ctx.Repo.Repository.ID, ctx.Repo.Owner.ID) if err != nil { ctx.Error(http.StatusInternalServerError, "GetContributors", err) return diff --git a/routers/hat/user/user.go b/routers/hat/user/user.go new file mode 100644 index 0000000..b933c91 --- /dev/null +++ b/routers/hat/user/user.go @@ -0,0 +1,27 @@ +package user + +import ( + "net/http" + + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/timeutil" + hat_activities_models "code.gitlink.org.cn/Gitlink/gitea_hat.git/models/activities" +) + +func GetUserHeatmapData(ctx *context.APIContext) { + startAt := timeutil.TimeStamp(ctx.FormInt("start")) + endAt := timeutil.TimeStamp(ctx.FormInt("end")) + if startAt <= 0 || endAt <= 0 { + startAt = timeutil.TimeStampNow() - 31536000 + endAt = timeutil.TimeStampNow() + } + + heatmap, err := hat_activities_models.GetUserHeatmapDataByTimestampRange(ctx.ContextUser, nil, ctx.Doer, startAt, endAt) + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetUserHeatmapDataByTimestampRange", err) + return + } + + ctx.JSON(http.StatusOK, heatmap) + +}