85 lines
3.1 KiB
Go
85 lines
3.1 KiB
Go
// Copyright 2018 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package activities
|
|
|
|
import (
|
|
"context"
|
|
|
|
"code.gitea.io/gitea/models/db"
|
|
"code.gitea.io/gitea/models/organization"
|
|
user_model "code.gitea.io/gitea/models/user"
|
|
"code.gitea.io/gitea/modules/setting"
|
|
"code.gitea.io/gitea/modules/structs"
|
|
"code.gitea.io/gitea/modules/timeutil"
|
|
)
|
|
|
|
// UserHeatmapData represents the data needed to create a heatmap
|
|
type UserHeatmapData struct {
|
|
Timestamp timeutil.TimeStamp `json:"timestamp"`
|
|
Contributions int64 `json:"contributions"`
|
|
}
|
|
|
|
// GetUserHeatmapDataByUser returns an array of UserHeatmapData, it checks whether doer can access user's activity
|
|
func GetUserHeatmapDataByUser(ctx context.Context, user, doer *user_model.User) ([]*UserHeatmapData, error) {
|
|
return getUserHeatmapData(ctx, user, nil, doer)
|
|
}
|
|
|
|
// GetUserHeatmapDataByOrgTeam returns an array of UserHeatmapData, it checks whether doer can access org's activity
|
|
func GetUserHeatmapDataByOrgTeam(ctx context.Context, org *organization.Organization, team *organization.Team, doer *user_model.User) ([]*UserHeatmapData, error) {
|
|
return getUserHeatmapData(ctx, org.AsUser(), team, doer)
|
|
}
|
|
|
|
func getUserHeatmapData(ctx context.Context, user *user_model.User, team *organization.Team, doer *user_model.User) ([]*UserHeatmapData, error) {
|
|
hdata := make([]*UserHeatmapData, 0)
|
|
|
|
if !ActivityReadable(user, doer) {
|
|
return hdata, nil
|
|
}
|
|
|
|
// Group by 15 minute intervals which will allow the client to accurately shift the timestamp to their timezone.
|
|
// The interval is based on the fact that there are timezones such as UTC +5:30 and UTC +12:45.
|
|
groupBy := "author_unix / 900 * 900"
|
|
groupByName := "timestamp" // We need this extra case because mssql doesn't allow grouping by alias
|
|
switch {
|
|
case setting.Database.Type.IsMySQL():
|
|
groupBy = "author_unix DIV 900 * 900"
|
|
case setting.Database.Type.IsMSSQL():
|
|
groupByName = groupBy
|
|
}
|
|
|
|
includePrivate, err := user_model.GetIncludePrivateContributions(ctx, user.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sess := db.GetEngine(ctx).
|
|
Select(groupBy+" AS timestamp, count(heatmap_contribution.user_id) as contributions").
|
|
Table("heatmap_contribution").
|
|
Join("INNER", "repository", "repository.id = heatmap_contribution.repo_id").
|
|
Join("INNER", "`user` AS repo_owner", "repo_owner.id = repository.owner_id").
|
|
Where("author_unix > ?", timeutil.TimeStampNow()-(366+7)*86400). // (366+7) days to include the first week for the heatmap
|
|
And("author_unix <= ?", timeutil.TimeStampNow())
|
|
|
|
if !includePrivate {
|
|
sess = sess.And("repository.is_private = ?", false).
|
|
And("repo_owner.visibility = ?", structs.VisibleTypePublic)
|
|
}
|
|
|
|
if user.IsOrganization() {
|
|
sess = sess.And("repository.owner_id = ?", user.ID)
|
|
if team != nil && !team.IncludesAllRepositories {
|
|
sess = sess.Join("INNER", "team_repo", "team_repo.repo_id = repository.id").
|
|
And("team_repo.org_id = ?", team.OrgID).
|
|
And("team_repo.team_id = ?", team.ID)
|
|
}
|
|
} else {
|
|
sess = sess.And("heatmap_contribution.user_id = ?", user.ID)
|
|
}
|
|
|
|
return hdata, sess.
|
|
GroupBy(groupByName).
|
|
OrderBy("timestamp").
|
|
Find(&hdata)
|
|
}
|