新增:用户提交次数分布api(按时间)

This commit is contained in:
yystopf 2022-12-20 18:16:44 +08:00
parent 2f56447e7a
commit b18906605a
6 changed files with 208 additions and 29 deletions

View File

@ -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
}

114
models/activities/action.go Normal file
View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
})

View File

@ -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

27
routers/hat/user/user.go Normal file
View File

@ -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)
}