Skip to content

Commit 6759237

Browse files
kolaentelunny
authored andcommitted
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 <[email protected]> * 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
1 parent f38fce9 commit 6759237

File tree

27 files changed

+649
-1
lines changed

27 files changed

+649
-1
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
193193
- `RECAPTCHA_SECRET`: **""**: Go to https://www.google.com/recaptcha/admin to get a secret for recaptcha.
194194
- `RECAPTCHA_SITEKEY`: **""**: Go to https://www.google.com/recaptcha/admin to get a sitekey for recaptcha.
195195
- `DEFAULT_ENABLE_DEPENDENCIES`: **true** Enable this to have dependencies enabled by default.
196+
- `ENABLE_USER_HEATMAP`: **true** Enable this to display the heatmap on users profiles.
196197

197198
## Webhook (`webhook`)
198199

integrations/api_user_heatmap_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2018 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.package models
4+
5+
package integrations
6+
7+
import (
8+
"code.gitea.io/gitea/models"
9+
"fmt"
10+
"github.com/stretchr/testify/assert"
11+
"net/http"
12+
"testing"
13+
)
14+
15+
func TestUserHeatmap(t *testing.T) {
16+
prepareTestEnv(t)
17+
adminUsername := "user1"
18+
normalUsername := "user2"
19+
session := loginUser(t, adminUsername)
20+
21+
urlStr := fmt.Sprintf("/api/v1/users/%s/heatmap", normalUsername)
22+
req := NewRequest(t, "GET", urlStr)
23+
resp := session.MakeRequest(t, req, http.StatusOK)
24+
var heatmap []*models.UserHeatmapData
25+
DecodeJSON(t, resp, &heatmap)
26+
var dummyheatmap []*models.UserHeatmapData
27+
dummyheatmap = append(dummyheatmap, &models.UserHeatmapData{Timestamp: 1540080000, Contributions: 1})
28+
29+
assert.Equal(t, dummyheatmap, heatmap)
30+
}

models/fixtures/action.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
act_user_id: 2
66
repo_id: 2
77
is_private: true
8+
created_unix: 1540139562
89

910
-
1011
id: 2

models/unit_tests.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ func MainTest(m *testing.M, pathToGiteaRoot string) {
4848
setting.RunUser = "runuser"
4949
setting.SSH.Port = 3000
5050
setting.SSH.Domain = "try.gitea.io"
51+
setting.UseSQLite3 = true
5152
setting.RepoRootPath, err = ioutil.TempDir(os.TempDir(), "repos")
5253
if err != nil {
5354
fatalTestError("TempDir: %v\n", err)

models/user_heatmap.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2018 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.package models
4+
5+
package models
6+
7+
import (
8+
"code.gitea.io/gitea/modules/setting"
9+
"code.gitea.io/gitea/modules/util"
10+
)
11+
12+
// UserHeatmapData represents the data needed to create a heatmap
13+
type UserHeatmapData struct {
14+
Timestamp util.TimeStamp `json:"timestamp"`
15+
Contributions int64 `json:"contributions"`
16+
}
17+
18+
// GetUserHeatmapDataByUser returns an array of UserHeatmapData
19+
func GetUserHeatmapDataByUser(user *User) (hdata []*UserHeatmapData, err error) {
20+
var groupBy string
21+
switch {
22+
case setting.UseSQLite3:
23+
groupBy = "strftime('%s', strftime('%Y-%m-%d', created_unix, 'unixepoch'))"
24+
case setting.UseMySQL:
25+
groupBy = "UNIX_TIMESTAMP(DATE_FORMAT(FROM_UNIXTIME(created_unix), '%Y%m%d'))"
26+
case setting.UsePostgreSQL:
27+
groupBy = "extract(epoch from date_trunc('day', to_timestamp(created_unix)))"
28+
case setting.UseMSSQL:
29+
groupBy = "dateadd(DAY,0, datediff(day,0, dateadd(s, created_unix, '19700101')))"
30+
}
31+
32+
err = x.Select(groupBy+" as timestamp, count(user_id) as contributions").
33+
Table("action").
34+
Where("user_id = ?", user.ID).
35+
And("created_unix > ?", (util.TimeStampNow() - 31536000)).
36+
GroupBy("timestamp").
37+
OrderBy("timestamp").
38+
Find(&hdata)
39+
return
40+
}

models/user_heatmap_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2018 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.package models
4+
5+
package models
6+
7+
import (
8+
"github.com/stretchr/testify/assert"
9+
"testing"
10+
)
11+
12+
func TestGetUserHeatmapDataByUser(t *testing.T) {
13+
// Prepare
14+
assert.NoError(t, PrepareTestDatabase())
15+
16+
// Insert some action
17+
user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
18+
19+
// get the action for comparison
20+
actions, err := GetFeeds(GetFeedsOptions{
21+
RequestedUser: user,
22+
RequestingUserID: user.ID,
23+
IncludePrivate: true,
24+
OnlyPerformedBy: false,
25+
IncludeDeleted: true,
26+
})
27+
assert.NoError(t, err)
28+
29+
// Get the heatmap and compare
30+
heatmap, err := GetUserHeatmapDataByUser(user)
31+
assert.NoError(t, err)
32+
assert.Equal(t, len(actions), len(heatmap))
33+
}

modules/setting/setting.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,6 +1218,7 @@ var Service struct {
12181218
DefaultEnableDependencies bool
12191219
DefaultAllowOnlyContributorsToTrackTime bool
12201220
NoReplyAddress string
1221+
EnableUserHeatmap bool
12211222

12221223
// OpenID settings
12231224
EnableOpenIDSignIn bool
@@ -1249,6 +1250,7 @@ func newService() {
12491250
Service.DefaultEnableDependencies = sec.Key("DEFAULT_ENABLE_DEPENDENCIES").MustBool(true)
12501251
Service.DefaultAllowOnlyContributorsToTrackTime = sec.Key("DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME").MustBool(true)
12511252
Service.NoReplyAddress = sec.Key("NO_REPLY_ADDRESS").MustString("noreply.example.org")
1253+
Service.EnableUserHeatmap = sec.Key("ENABLE_USER_HEATMAP").MustBool(true)
12521254

12531255
sec = Cfg.Section("openid")
12541256
Service.EnableOpenIDSignIn = sec.Key("ENABLE_OPENID_SIGNIN").MustBool(!InstallLock)

options/locale/locale_en-US.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ starred = Starred Repositories
320320
following = Following
321321
follow = Follow
322322
unfollow = Unfollow
323+
heatmap.loading = Loading Heatmap…
323324
324325
form.name_reserved = The username '%s' is reserved.
325326
form.name_pattern_not_allowed = The pattern '%s' is not allowed in a username.

public/css/index.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/less/_base.less

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,3 +588,20 @@ footer {
588588
border-bottom-width: 0 !important;
589589
margin-bottom: 2px !important;
590590
}
591+
592+
#user-heatmap{
593+
width: 107%; // Fixes newest contributions not showing
594+
text-align: center;
595+
margin: 40px 0 30px;
596+
597+
svg:not(:root) {
598+
overflow: inherit;
599+
padding: 0 !important;
600+
}
601+
602+
@media only screen and (max-width: 1200px) {
603+
& {
604+
display: none;
605+
}
606+
}
607+
}

public/less/_user.less

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@
5858
.ui.repository.list {
5959
margin-top: 25px;
6060
}
61+
62+
#loading-heatmap{
63+
margin-bottom: 1em;
64+
}
6165
}
6266

6367
&.followers {

public/vendor/VERSIONS

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,12 @@ Version: 4.3.0
5858

5959
File(s): /vendor/assets/swagger-ui/
6060
Version: 3.0.4
61+
62+
File(s): /vendor/plugins/d3/
63+
Version: 4.13.0
64+
65+
File(s): /vendor/plugins/calendar-heatmap/
66+
Version: 337b431
67+
68+
File(s): /vendor/plugins/moment/
69+
Version: 2.22.2

public/vendor/librejs.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,21 @@
135135
<td><a href="https://github.com/swagger-api/swagger-ui/blob/master/LICENSE">Apache-2.0</a></td>
136136
<td><a href="https://github.com/swagger-api/swagger-ui/archive/v3.0.4.tar.gz">swagger-ui-v3.0.4.tar.gz</a></td>
137137
</tr>
138+
<tr>
139+
<td><a href="./plugins/d3/">d3</a></td>
140+
<td><a href="https://github.com/d3/d3/blob/master/LICENSE">BSD 3-Clause</a></td>
141+
<td><a href="https://github.com/d3/d3/releases/download/v4.13.0/d3.zip">d3.zip</a></td>
142+
</tr>
143+
<tr>
144+
<td><a href="./plugins/calendar-heatmap/">calendar-heatmap</a></td>
145+
<td><a href="https://github.com/DKirwan/calendar-heatmap/blob/master/LICENSE">MIT</a></td>
146+
<td><a href="https://github.com/DKirwan/calendar-heatmap/archive/master.zip">337b431.zip</a></td>
147+
</tr>
148+
<tr>
149+
<td><a href="./plugins/moment/">moment.js</a></td>
150+
<td><a href="https://github.com/moment/moment/blob/develop/LICENSE">MIT</a></td>
151+
<td><a href="https://github.com/moment/moment/archive/2.22.2.tar.gz">0.4.1.tar.gz</a></td>
152+
</tr>
138153
</tbody>
139154
</table>
140155
</body>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
text.month-name,
2+
text.calendar-heatmap-legend-text,
3+
text.day-initial {
4+
font-size: 10px;
5+
fill: inherit;
6+
font-family: Helvetica, arial, 'Open Sans', sans-serif;
7+
}
8+
rect.day-cell:hover {
9+
stroke: #555555;
10+
stroke-width: 1px;
11+
}
12+
.day-cell-tooltip {
13+
position: absolute;
14+
z-index: 9999;
15+
padding: 5px 9px;
16+
color: #bbbbbb;
17+
font-size: 12px;
18+
background: rgba(0, 0, 0, 0.85);
19+
border-radius: 3px;
20+
text-align: center;
21+
}
22+
.day-cell-tooltip > span {
23+
font-family: Helvetica, arial, 'Open Sans', sans-serif
24+
}
25+
.calendar-heatmap {
26+
box-sizing: initial;
27+
}

0 commit comments

Comments
 (0)