Skip to content

Commit 1c46a48

Browse files
committed
Merge remote-tracking branch 'upstream/pull/6046'
2 parents 4566385 + 6abba80 commit 1c46a48

File tree

9 files changed

+220
-175
lines changed

9 files changed

+220
-175
lines changed

app/assets/javascripts/heatmap.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
$(function () {
1+
$(document).on("turbo:frame-load", function () {
22
const heatmap = $(".heatmap").removeClass("d-none").addClass("d-grid");
33
const weekInfo = getWeekInfo();
44
const maxPerDay = heatmap.data("max-per-day");
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
module Users
2+
class HeatmapsController < ApplicationController
3+
layout false
4+
5+
before_action :authorize_web
6+
before_action :set_locale
7+
before_action :check_database_readable
8+
9+
authorize_resource :user
10+
11+
def show
12+
@user = User.find_by(:display_name => params[:user_display_name])
13+
14+
if @user && (@user.visible? || current_user&.administrator?)
15+
@heatmap_data = Rails.cache.fetch("heatmap_data_of_user_#{@user.id}", :expires_at => Time.zone.now.end_of_day) do
16+
from = 1.year.ago.beginning_of_day
17+
to = Time.zone.now.end_of_day
18+
19+
mapped = Changeset
20+
.where(:user_id => @user.id)
21+
.where(:created_at => from..to)
22+
.where(:num_changes => 1..)
23+
.group("date_trunc('day', created_at)")
24+
.select("date_trunc('day', created_at) AS date, SUM(num_changes) AS total_changes, MAX(id) AS max_id")
25+
.order("date")
26+
.map do |changeset|
27+
{
28+
:date => changeset.date.to_date,
29+
:total_changes => changeset.total_changes.to_i,
30+
:max_id => changeset.max_id
31+
}
32+
end
33+
34+
{
35+
:count => mapped.sum { |entry| entry[:total_changes] },
36+
:data => mapped.index_by { |entry| entry[:date] },
37+
:from => from,
38+
:to => to
39+
}
40+
end
41+
end
42+
end
43+
end
44+
end

app/controllers/users_controller.rb

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -23,33 +23,7 @@ def show
2323

2424
if @user && (@user.visible? || current_user&.administrator?)
2525
@title = @user.display_name
26-
27-
@heatmap_data = Rails.cache.fetch("heatmap_data_of_user_#{@user.id}", :expires_at => Time.zone.now.end_of_day) do
28-
from = 1.year.ago.beginning_of_day
29-
to = Time.zone.now.end_of_day
30-
31-
mapped = Changeset
32-
.where(:user_id => @user.id)
33-
.where(:created_at => from..to)
34-
.where(:num_changes => 1..)
35-
.group("date_trunc('day', created_at)")
36-
.select("date_trunc('day', created_at) AS date, SUM(num_changes) AS total_changes, MAX(id) AS max_id")
37-
.order("date")
38-
.map do |changeset|
39-
{
40-
:date => changeset.date.to_date,
41-
:total_changes => changeset.total_changes.to_i,
42-
:max_id => changeset.max_id
43-
}
44-
end
45-
46-
{
47-
:count => mapped.sum { |entry| entry[:total_changes] },
48-
:data => mapped.index_by { |entry| entry[:date] },
49-
:from => from,
50-
:to => to
51-
}
52-
end
26+
@heatmap_frame = true
5327
else
5428
render_unknown_user params[:display_name]
5529
end

app/views/users/_heatmap.html.erb

Lines changed: 0 additions & 23 deletions
This file was deleted.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<%= turbo_frame_tag "#{dom_id(@user)}_heatmap", :data => { :turbo => false } do %>
2+
<% if @heatmap_data[:count].positive? %>
3+
<h2 class="text-body-secondary fs-5 mt-4"><%= t("users.show.contributions", :count => @heatmap_data[:count]) %></h2>
4+
<% cal_data = prepare_heatmap(@heatmap_data[:data], @heatmap_data[:from], @heatmap_data[:to]) %>
5+
<div class="row">
6+
<div class="col overflow-auto">
7+
<div class="heatmap d-none align-items-center mb-1 text-center" data-max-per-day="<%= cal_data[:max_per_day] %>">
8+
<!-- Months -->
9+
<% cal_data[:months].each do |month| %>
10+
<span class="mb-1 mx-n2" data-month="<%= month %>"><%= t("date.abbr_month_names")[((month - 1) % 12) + 1] %></span>
11+
<% end %>
12+
<!-- Days -->
13+
<% (0..6).each do |day| %>
14+
<span class="me-1 my-n1" data-weekday="<%= day %>"><%= t("date.abbr_day_names")[day] %></span>
15+
<% end %>
16+
<!-- Heatmap -->
17+
<% cal_data[:days].each do |day| %>
18+
<% if day[:total_changes] == 0 %>
19+
<span data-date="<%= day[:date] %>"></span>
20+
<% else %>
21+
<a href="<%= user_history_path @user, :before => day[:max_id] + 1 %>" data-date="<%= day[:date] %>" data-count="<%= day[:total_changes] %>"><span></span></a>
22+
<% end %>
23+
<% end %>
24+
</div>
25+
</div>
26+
</div>
27+
<% end %>
28+
<% end %>

app/views/users/show.html.erb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,9 +257,8 @@
257257
</div>
258258
<% end %>
259259

260-
<% if @heatmap_data[:count].positive? %>
261-
<h2 class="text-body-secondary fs-5 mt-4"><%= t("users.show.contributions", :count => @heatmap_data[:count]) %></h2>
262-
<%= render :partial => "heatmap", :locals => @heatmap_data %>
260+
<% if @heatmap_frame %>
261+
<%= turbo_frame_tag "#{dom_id(@user)}_heatmap", :src => user_heatmap_path(@user), :data => { :turbo => false } %>
263262
<% end %>
264263

265264
<%= render :partial => "diary_entries/profile_diaries", :locals => { :diary_entries => @user.diary_entries.visible.order(:created_at => :desc).limit(4) } %>

config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@
293293
resources :users, :path => "user", :param => :display_name, :only => [:new, :create, :show] do
294294
resource :role, :controller => "user_roles", :path => "roles/:role", :only => [:create, :destroy]
295295
scope :module => :users do
296+
resource :heatmap, :only => :show
296297
resources :diary_comments, :only => :index
297298
resources :changeset_comments, :only => :index
298299
resource :issued_blocks, :path => "blocks_by", :only => :show
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
require "test_helper"
2+
3+
module Users
4+
class HeatmapsControllerTest < ActionDispatch::IntegrationTest
5+
##
6+
# test all routes which lead to this controller
7+
def test_routes
8+
assert_routing(
9+
{ :path => "/user/username/heatmap", :method => :get },
10+
{ :controller => "users/heatmaps", :action => "show", :user_display_name => "username" }
11+
)
12+
end
13+
14+
def test_show_data
15+
user = create(:user)
16+
# Create two changesets
17+
create(:changeset, :user => user, :created_at => 6.months.ago, :num_changes => 10)
18+
create(:changeset, :user => user, :created_at => 3.months.ago, :num_changes => 20)
19+
20+
get user_heatmap_path(user)
21+
22+
assert_response :success
23+
# The data should not be empty
24+
heatmap_data = assigns(:heatmap_data)
25+
assert_not_nil heatmap_data
26+
assert_predicate heatmap_data[:data], :any?
27+
# The data should be in the right format
28+
heatmap_data[:data].each_value do |entry|
29+
assert_equal [:date, :max_id, :total_changes], entry.keys.sort, "Heatmap data entries should have expected keys"
30+
end
31+
assert_equal 30, heatmap_data[:count]
32+
end
33+
34+
def test_show_data_caching
35+
# Enable caching to be able to test
36+
Rails.cache.clear
37+
@original_cache_store = Rails.cache
38+
Rails.cache = ActiveSupport::Cache::MemoryStore.new
39+
40+
user = create(:user)
41+
42+
# Create an initial changeset
43+
create(:changeset, :user => user, :created_at => 6.months.ago, :num_changes => 15)
44+
45+
# First request to populate the cache
46+
get user_heatmap_path(user)
47+
first_response_data = assigns(:heatmap_data)
48+
assert_not_nil first_response_data, "Expected heatmap data to be assigned on the first request"
49+
assert_equal 1, first_response_data[:data].values.count { |day| day[:total_changes].positive? }, "Expected one entry in the heatmap data"
50+
51+
# Inspect cache after the first request
52+
cached_data = Rails.cache.read("heatmap_data_of_user_#{user.id}")
53+
assert_equal first_response_data, cached_data, "Expected the cache to contain the first response data"
54+
55+
# Add a new changeset to the database
56+
create(:changeset, :user => user, :created_at => 3.months.ago, :num_changes => 20)
57+
58+
# Second request
59+
get user_heatmap_path(user)
60+
second_response_data = assigns(:heatmap_data)
61+
62+
# Confirm that the cache is still being used
63+
assert_equal first_response_data, second_response_data, "Expected cached data to be returned on the second request"
64+
65+
# Clear the cache and make a third request to confirm new data is retrieved
66+
Rails.cache.clear
67+
get user_heatmap_path(user)
68+
third_response_data = assigns(:heatmap_data)
69+
70+
# Ensure the new entry is now included
71+
assert_equal 2, third_response_data[:data].values.count { |day| day[:total_changes].positive? }, "Expected two entries in the heatmap data after clearing the cache"
72+
73+
# Reset caching config to defaults
74+
Rails.cache.clear
75+
Rails.cache = @original_cache_store
76+
end
77+
78+
def test_show_data_no_changesets
79+
user = create(:user)
80+
81+
get user_heatmap_path(user)
82+
83+
assert_response :success
84+
assert_empty(assigns(:heatmap_data)[:data].values)
85+
assert_select ".heatmap", :count => 0
86+
end
87+
88+
def test_show_rendering_of_user_with_no_changesets
89+
user_without_changesets = create(:user)
90+
91+
get user_heatmap_path(user_without_changesets)
92+
93+
assert_response :success
94+
assert_select ".heatmap", 0
95+
end
96+
97+
def test_show_rendering_of_user_with_changesets
98+
user = create(:user)
99+
changeset39 = create(:changeset, :user => user, :created_at => 4.months.ago.beginning_of_day, :num_changes => 39)
100+
_changeset5 = create(:changeset, :user => user, :created_at => 3.months.ago.beginning_of_day, :num_changes => 5)
101+
changeset11 = create(:changeset, :user => user, :created_at => 3.months.ago.beginning_of_day, :num_changes => 11)
102+
103+
get user_heatmap_path(user)
104+
105+
assert_response :success
106+
assert_select ".heatmap a", 2
107+
108+
history_path = user_history_path(user)
109+
assert_select ".heatmap a[data-date='#{4.months.ago.to_date}'][data-count='39'][href='#{history_path}?before=#{changeset39.id + 1}']"
110+
assert_select ".heatmap a[data-date='#{3.months.ago.to_date}'][data-count='16'][href='#{history_path}?before=#{changeset11.id + 1}']"
111+
assert_select ".heatmap [data-date='#{5.months.ago.to_date}']:not([data-count])"
112+
end
113+
114+
def test_headline_changeset_zero
115+
user = create(:user)
116+
117+
get user_heatmap_path(user)
118+
119+
assert_response :success
120+
assert_select "h2.text-body-secondary.fs-5", :count => 0
121+
end
122+
123+
def test_headline_changeset_singular
124+
user = create(:user)
125+
create(:changeset, :user => user, :created_at => 4.months.ago.beginning_of_day, :num_changes => 1)
126+
127+
get user_heatmap_path(user)
128+
129+
assert_response :success
130+
assert_select "h2.text-body-secondary.fs-5", :text => "1 contribution in the last year"
131+
end
132+
133+
def test_headline_changeset_plural
134+
user = create(:user)
135+
create(:changeset, :user => user, :created_at => 4.months.ago.beginning_of_day, :num_changes => 12)
136+
137+
get user_heatmap_path(user)
138+
139+
assert_response :success
140+
assert_select "h2.text-body-secondary.fs-5", :text => "12 contributions in the last year"
141+
end
142+
end
143+
end

0 commit comments

Comments
 (0)