User action heatmap (#5131)

* Added basic heatmap data

* Added extra case for sqlite

* Built basic heatmap into user profile

* Get contribution data from api & styling

* Fixed lint & added extra group by statements for all database types

* generated swagger spec

* generated swagger spec

* generated swagger spec

* fixed swagger spec

* fmt

* Added tests

* Added setting to enable/disable user heatmap

* Added locale for loading text

* Removed UseTiDB

* Updated librejs & moment.js

* Fixed import order

* Fixed heatmap in postgresql

* Update docs/content/doc/advanced/config-cheat-sheet.en-us.md

Co-Authored-By: kolaente <konrad@kola-entertainments.de>

* Added copyright header

* Fixed a bug to show the heatmap for the actual user instead of the currently logged in

* Added integration test for heatmaps

* Added a heatmap on the dashboard

* Fixed timestamp parsing

* Hide heatmap on mobile

* optimized postgresql group by query

* Improved sqlite group by statement
This commit is contained in:
kolaente 2018-10-23 04:57:42 +02:00 committed by Lunny Xiao
parent f38fce916e
commit 6759237eda
27 changed files with 649 additions and 1 deletions

View file

@ -5,6 +5,7 @@
act_user_id: 2
repo_id: 2
is_private: true
created_unix: 1540139562
-
id: 2

View file

@ -48,6 +48,7 @@ func MainTest(m *testing.M, pathToGiteaRoot string) {
setting.RunUser = "runuser"
setting.SSH.Port = 3000
setting.SSH.Domain = "try.gitea.io"
setting.UseSQLite3 = true
setting.RepoRootPath, err = ioutil.TempDir(os.TempDir(), "repos")
if err != nil {
fatalTestError("TempDir: %v\n", err)

40
models/user_heatmap.go Normal file
View file

@ -0,0 +1,40 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.package models
package models
import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
)
// UserHeatmapData represents the data needed to create a heatmap
type UserHeatmapData struct {
Timestamp util.TimeStamp `json:"timestamp"`
Contributions int64 `json:"contributions"`
}
// GetUserHeatmapDataByUser returns an array of UserHeatmapData
func GetUserHeatmapDataByUser(user *User) (hdata []*UserHeatmapData, err error) {
var groupBy string
switch {
case setting.UseSQLite3:
groupBy = "strftime('%s', strftime('%Y-%m-%d', created_unix, 'unixepoch'))"
case setting.UseMySQL:
groupBy = "UNIX_TIMESTAMP(DATE_FORMAT(FROM_UNIXTIME(created_unix), '%Y%m%d'))"
case setting.UsePostgreSQL:
groupBy = "extract(epoch from date_trunc('day', to_timestamp(created_unix)))"
case setting.UseMSSQL:
groupBy = "dateadd(DAY,0, datediff(day,0, dateadd(s, created_unix, '19700101')))"
}
err = x.Select(groupBy+" as timestamp, count(user_id) as contributions").
Table("action").
Where("user_id = ?", user.ID).
And("created_unix > ?", (util.TimeStampNow() - 31536000)).
GroupBy("timestamp").
OrderBy("timestamp").
Find(&hdata)
return
}

View file

@ -0,0 +1,33 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.package models
package models
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestGetUserHeatmapDataByUser(t *testing.T) {
// Prepare
assert.NoError(t, PrepareTestDatabase())
// Insert some action
user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
// get the action for comparison
actions, err := GetFeeds(GetFeedsOptions{
RequestedUser: user,
RequestingUserID: user.ID,
IncludePrivate: true,
OnlyPerformedBy: false,
IncludeDeleted: true,
})
assert.NoError(t, err)
// Get the heatmap and compare
heatmap, err := GetUserHeatmapDataByUser(user)
assert.NoError(t, err)
assert.Equal(t, len(actions), len(heatmap))
}