Skip to content

Commit

Permalink
User action heatmap (#5131)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
kolaente authored and lunny committed Oct 23, 2018
1 parent f38fce9 commit 6759237
Show file tree
Hide file tree
Showing 27 changed files with 649 additions and 1 deletion.
1 change: 1 addition & 0 deletions docs/content/doc/advanced/config-cheat-sheet.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
- `RECAPTCHA_SECRET`: **""**: Go to https://www.google.com/recaptcha/admin to get a secret for recaptcha.
- `RECAPTCHA_SITEKEY`: **""**: Go to https://www.google.com/recaptcha/admin to get a sitekey for recaptcha.
- `DEFAULT_ENABLE_DEPENDENCIES`: **true** Enable this to have dependencies enabled by default.
- `ENABLE_USER_HEATMAP`: **true** Enable this to display the heatmap on users profiles.

## Webhook (`webhook`)

Expand Down
30 changes: 30 additions & 0 deletions integrations/api_user_heatmap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// 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 integrations

import (
"code.gitea.io/gitea/models"
"fmt"
"github.com/stretchr/testify/assert"
"net/http"
"testing"
)

func TestUserHeatmap(t *testing.T) {
prepareTestEnv(t)
adminUsername := "user1"
normalUsername := "user2"
session := loginUser(t, adminUsername)

urlStr := fmt.Sprintf("/api/v1/users/%s/heatmap", normalUsername)
req := NewRequest(t, "GET", urlStr)
resp := session.MakeRequest(t, req, http.StatusOK)
var heatmap []*models.UserHeatmapData
DecodeJSON(t, resp, &heatmap)
var dummyheatmap []*models.UserHeatmapData
dummyheatmap = append(dummyheatmap, &models.UserHeatmapData{Timestamp: 1540080000, Contributions: 1})

assert.Equal(t, dummyheatmap, heatmap)
}
1 change: 1 addition & 0 deletions models/fixtures/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
act_user_id: 2
repo_id: 2
is_private: true
created_unix: 1540139562

-
id: 2
Expand Down
1 change: 1 addition & 0 deletions models/unit_tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
40 changes: 40 additions & 0 deletions models/user_heatmap.go
Original file line number Diff line number Diff line change
@@ -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
}
33 changes: 33 additions & 0 deletions models/user_heatmap_test.go
Original file line number Diff line number Diff line change
@@ -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))
}
2 changes: 2 additions & 0 deletions modules/setting/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -1218,6 +1218,7 @@ var Service struct {
DefaultEnableDependencies bool
DefaultAllowOnlyContributorsToTrackTime bool
NoReplyAddress string
EnableUserHeatmap bool

// OpenID settings
EnableOpenIDSignIn bool
Expand Down Expand Up @@ -1249,6 +1250,7 @@ func newService() {
Service.DefaultEnableDependencies = sec.Key("DEFAULT_ENABLE_DEPENDENCIES").MustBool(true)
Service.DefaultAllowOnlyContributorsToTrackTime = sec.Key("DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME").MustBool(true)
Service.NoReplyAddress = sec.Key("NO_REPLY_ADDRESS").MustString("noreply.example.org")
Service.EnableUserHeatmap = sec.Key("ENABLE_USER_HEATMAP").MustBool(true)

sec = Cfg.Section("openid")
Service.EnableOpenIDSignIn = sec.Key("ENABLE_OPENID_SIGNIN").MustBool(!InstallLock)
Expand Down
1 change: 1 addition & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ starred = Starred Repositories
following = Following
follow = Follow
unfollow = Unfollow
heatmap.loading = Loading Heatmap…
form.name_reserved = The username '%s' is reserved.
form.name_pattern_not_allowed = The pattern '%s' is not allowed in a username.
Expand Down
2 changes: 1 addition & 1 deletion public/css/index.css

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions public/less/_base.less
Original file line number Diff line number Diff line change
Expand Up @@ -588,3 +588,20 @@ footer {
border-bottom-width: 0 !important;
margin-bottom: 2px !important;
}

#user-heatmap{
width: 107%; // Fixes newest contributions not showing
text-align: center;
margin: 40px 0 30px;

svg:not(:root) {
overflow: inherit;
padding: 0 !important;
}

@media only screen and (max-width: 1200px) {
& {
display: none;
}
}
}
4 changes: 4 additions & 0 deletions public/less/_user.less
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@
.ui.repository.list {
margin-top: 25px;
}

#loading-heatmap{
margin-bottom: 1em;
}
}

&.followers {
Expand Down
9 changes: 9 additions & 0 deletions public/vendor/VERSIONS
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,12 @@ Version: 4.3.0

File(s): /vendor/assets/swagger-ui/
Version: 3.0.4

File(s): /vendor/plugins/d3/
Version: 4.13.0

File(s): /vendor/plugins/calendar-heatmap/
Version: 337b431

File(s): /vendor/plugins/moment/
Version: 2.22.2
15 changes: 15 additions & 0 deletions public/vendor/librejs.html
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,21 @@
<td><a href="https://github.com/swagger-api/swagger-ui/blob/master/LICENSE">Apache-2.0</a></td>
<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>
</tr>
<tr>
<td><a href="./plugins/d3/">d3</a></td>
<td><a href="https://github.com/d3/d3/blob/master/LICENSE">BSD 3-Clause</a></td>
<td><a href="https://github.com/d3/d3/releases/download/v4.13.0/d3.zip">d3.zip</a></td>
</tr>
<tr>
<td><a href="./plugins/calendar-heatmap/">calendar-heatmap</a></td>
<td><a href="https://github.com/DKirwan/calendar-heatmap/blob/master/LICENSE">MIT</a></td>
<td><a href="https://github.com/DKirwan/calendar-heatmap/archive/master.zip">337b431.zip</a></td>
</tr>
<tr>
<td><a href="./plugins/moment/">moment.js</a></td>
<td><a href="https://github.com/moment/moment/blob/develop/LICENSE">MIT</a></td>
<td><a href="https://github.com/moment/moment/archive/2.22.2.tar.gz">0.4.1.tar.gz</a></td>
</tr>
</tbody>
</table>
</body>
Expand Down
27 changes: 27 additions & 0 deletions public/vendor/plugins/calendar-heatmap/calendar-heatmap.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
text.month-name,
text.calendar-heatmap-legend-text,
text.day-initial {
font-size: 10px;
fill: inherit;
font-family: Helvetica, arial, 'Open Sans', sans-serif;
}
rect.day-cell:hover {
stroke: #555555;
stroke-width: 1px;
}
.day-cell-tooltip {
position: absolute;
z-index: 9999;
padding: 5px 9px;
color: #bbbbbb;
font-size: 12px;
background: rgba(0, 0, 0, 0.85);
border-radius: 3px;
text-align: center;
}
.day-cell-tooltip > span {
font-family: Helvetica, arial, 'Open Sans', sans-serif
}
.calendar-heatmap {
box-sizing: initial;
}
Loading

0 comments on commit 6759237

Please sign in to comment.