From d36b3d7eb76a706a01fe0612f4d361b7daf8223d Mon Sep 17 00:00:00 2001 From: Anton Khorev Date: Fri, 23 May 2025 18:19:41 +0300 Subject: [PATCH] Move heatmap to Turbo frame --- app/assets/javascripts/heatmap.js | 2 +- app/controllers/users/heatmaps_controller.rb | 44 +++++++++++++++++++ app/controllers/users_controller.rb | 28 +----------- app/views/users/_heatmap.html.erb | 23 ---------- app/views/users/heatmaps/show.html.erb | 28 ++++++++++++ app/views/users/show.html.erb | 5 +-- config/routes.rb | 1 + .../users/heatmaps_controller_test.rb | 14 ++++++ test/controllers/users_controller_test.rb | 21 ++++----- 9 files changed, 102 insertions(+), 64 deletions(-) create mode 100644 app/controllers/users/heatmaps_controller.rb delete mode 100644 app/views/users/_heatmap.html.erb create mode 100644 app/views/users/heatmaps/show.html.erb create mode 100644 test/controllers/users/heatmaps_controller_test.rb diff --git a/app/assets/javascripts/heatmap.js b/app/assets/javascripts/heatmap.js index 15809cc0b..72d34042f 100644 --- a/app/assets/javascripts/heatmap.js +++ b/app/assets/javascripts/heatmap.js @@ -1,4 +1,4 @@ -$(function () { +$(document).on("turbo:frame-load", function () { const heatmap = $(".heatmap").removeClass("d-none").addClass("d-grid"); const weekInfo = getWeekInfo(); const maxPerDay = heatmap.data("max-per-day"); diff --git a/app/controllers/users/heatmaps_controller.rb b/app/controllers/users/heatmaps_controller.rb new file mode 100644 index 000000000..dd30395a3 --- /dev/null +++ b/app/controllers/users/heatmaps_controller.rb @@ -0,0 +1,44 @@ +module Users + class HeatmapsController < ApplicationController + layout false + + before_action :authorize_web + before_action :set_locale + before_action :check_database_readable + + authorize_resource :user + + def show + @user = User.find_by(:display_name => params[:user_display_name]) + + if @user && (@user.visible? || current_user&.administrator?) + @heatmap_data = Rails.cache.fetch("heatmap_data_of_user_#{@user.id}", :expires_at => Time.zone.now.end_of_day) do + from = 1.year.ago.beginning_of_day + to = Time.zone.now.end_of_day + + mapped = Changeset + .where(:user_id => @user.id) + .where(:created_at => from..to) + .where(:num_changes => 1..) + .group("date_trunc('day', created_at)") + .select("date_trunc('day', created_at) AS date, SUM(num_changes) AS total_changes, MAX(id) AS max_id") + .order("date") + .map do |changeset| + { + :date => changeset.date.to_date, + :total_changes => changeset.total_changes.to_i, + :max_id => changeset.max_id + } + end + + { + :count => mapped.sum { |entry| entry[:total_changes] }, + :data => mapped.index_by { |entry| entry[:date] }, + :from => from, + :to => to + } + end + end + end + end +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index de6c8b1c2..d4862c65c 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -23,33 +23,7 @@ class UsersController < ApplicationController if @user && (@user.visible? || current_user&.administrator?) @title = @user.display_name - - @heatmap_data = Rails.cache.fetch("heatmap_data_of_user_#{@user.id}", :expires_at => Time.zone.now.end_of_day) do - from = 1.year.ago.beginning_of_day - to = Time.zone.now.end_of_day - - mapped = Changeset - .where(:user_id => @user.id) - .where(:created_at => from..to) - .where(:num_changes => 1..) - .group("date_trunc('day', created_at)") - .select("date_trunc('day', created_at) AS date, SUM(num_changes) AS total_changes, MAX(id) AS max_id") - .order("date") - .map do |changeset| - { - :date => changeset.date.to_date, - :total_changes => changeset.total_changes.to_i, - :max_id => changeset.max_id - } - end - - { - :count => mapped.sum { |entry| entry[:total_changes] }, - :data => mapped.index_by { |entry| entry[:date] }, - :from => from, - :to => to - } - end + @heatmap_frame = true else render_unknown_user params[:display_name] end diff --git a/app/views/users/_heatmap.html.erb b/app/views/users/_heatmap.html.erb deleted file mode 100644 index 76d4d527c..000000000 --- a/app/views/users/_heatmap.html.erb +++ /dev/null @@ -1,23 +0,0 @@ -<% cal_data = prepare_heatmap(data, from, to) %> -
-
-
- - <% cal_data[:months].each do |month| %> - <%= t("date.abbr_month_names")[((month - 1) % 12) + 1] %> - <% end %> - - <% (0..6).each do |day| %> - <%= t("date.abbr_day_names")[day] %> - <% end %> - - <% cal_data[:days].each do |day| %> - <% if day[:total_changes] == 0 %> - - <% else %> - - <% end %> - <% end %> -
-
-
diff --git a/app/views/users/heatmaps/show.html.erb b/app/views/users/heatmaps/show.html.erb new file mode 100644 index 000000000..a0f400d81 --- /dev/null +++ b/app/views/users/heatmaps/show.html.erb @@ -0,0 +1,28 @@ +<%= turbo_frame_tag "#{dom_id(@user)}_heatmap", :data => { :turbo => false } do %> + <% if @heatmap_data[:count].positive? %> +

<%= t("users.show.contributions", :count => @heatmap_data[:count]) %>

+ <% cal_data = prepare_heatmap(@heatmap_data[:data], @heatmap_data[:from], @heatmap_data[:to]) %> +
+
+
+ + <% cal_data[:months].each do |month| %> + <%= t("date.abbr_month_names")[((month - 1) % 12) + 1] %> + <% end %> + + <% (0..6).each do |day| %> + <%= t("date.abbr_day_names")[day] %> + <% end %> + + <% cal_data[:days].each do |day| %> + <% if day[:total_changes] == 0 %> + + <% else %> + + <% end %> + <% end %> +
+
+
+ <% end %> +<% end %> diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index b2cdd1242..857facafc 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -257,9 +257,8 @@ <% end %> -<% if @heatmap_data[:count].positive? %> -

<%= t("users.show.contributions", :count => @heatmap_data[:count]) %>

- <%= render :partial => "heatmap", :locals => @heatmap_data %> +<% if @heatmap_frame %> + <%= turbo_frame_tag "#{dom_id(@user)}_heatmap", :src => user_heatmap_path(@user), :data => { :turbo => false } %> <% end %> <%= render :partial => "diary_entries/profile_diaries", :locals => { :diary_entries => @user.diary_entries.visible.order(:created_at => :desc).limit(4) } %> diff --git a/config/routes.rb b/config/routes.rb index 168b8dd08..436e9a308 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -293,6 +293,7 @@ OpenStreetMap::Application.routes.draw do resources :users, :path => "user", :param => :display_name, :only => [:new, :create, :show] do resource :role, :controller => "user_roles", :path => "roles/:role", :only => [:create, :destroy] scope :module => :users do + resource :heatmap, :only => :show resources :diary_comments, :only => :index resources :changeset_comments, :only => :index resource :issued_blocks, :path => "blocks_by", :only => :show diff --git a/test/controllers/users/heatmaps_controller_test.rb b/test/controllers/users/heatmaps_controller_test.rb new file mode 100644 index 000000000..6d0524e95 --- /dev/null +++ b/test/controllers/users/heatmaps_controller_test.rb @@ -0,0 +1,14 @@ +require "test_helper" + +module Users + class HeatmapsControllerTest < ActionDispatch::IntegrationTest + ## + # test all routes which lead to this controller + def test_routes + assert_routing( + { :path => "/user/username/heatmap", :method => :get }, + { :controller => "users/heatmaps", :action => "show", :user_display_name => "username" } + ) + end + end +end diff --git a/test/controllers/users_controller_test.rb b/test/controllers/users_controller_test.rb index 606a19dab..620435ec1 100644 --- a/test/controllers/users_controller_test.rb +++ b/test/controllers/users_controller_test.rb @@ -336,7 +336,7 @@ class UsersControllerTest < ActionDispatch::IntegrationTest create(:changeset, :user => user, :created_at => 6.months.ago, :num_changes => 10) create(:changeset, :user => user, :created_at => 3.months.ago, :num_changes => 20) - get user_path(user) + get user_heatmap_path(user) assert_response :success # The data should not be empty heatmap_data = assigns(:heatmap_data) @@ -361,7 +361,7 @@ class UsersControllerTest < ActionDispatch::IntegrationTest create(:changeset, :user => user, :created_at => 6.months.ago, :num_changes => 15) # First request to populate the cache - get user_path(user) + get user_heatmap_path(user) first_response_data = assigns(:heatmap_data) assert_not_nil first_response_data, "Expected heatmap data to be assigned on the first request" assert_equal 1, first_response_data[:data].values.count { |day| day[:total_changes].positive? }, "Expected one entry in the heatmap data" @@ -374,7 +374,7 @@ class UsersControllerTest < ActionDispatch::IntegrationTest create(:changeset, :user => user, :created_at => 3.months.ago, :num_changes => 20) # Second request - get user_path(user) + get user_heatmap_path(user) second_response_data = assigns(:heatmap_data) # Confirm that the cache is still being used @@ -382,7 +382,7 @@ class UsersControllerTest < ActionDispatch::IntegrationTest # Clear the cache and make a third request to confirm new data is retrieved Rails.cache.clear - get user_path(user) + get user_heatmap_path(user) third_response_data = assigns(:heatmap_data) # Ensure the new entry is now included @@ -396,7 +396,8 @@ class UsersControllerTest < ActionDispatch::IntegrationTest def test_show_heatmap_data_no_changesets user = create(:user) - get user_path(user) + get user_heatmap_path(user) + assert_response :success assert_empty(assigns(:heatmap_data)[:data].values) assert_select ".heatmap", :count => 0 @@ -405,7 +406,7 @@ class UsersControllerTest < ActionDispatch::IntegrationTest def test_show_heatmap_rendering_of_user_with_no_changesets user_without_changesets = create(:user) - get user_path(user_without_changesets) + get user_heatmap_path(user_without_changesets) assert_response :success assert_select ".heatmap", 0 @@ -417,7 +418,7 @@ class UsersControllerTest < ActionDispatch::IntegrationTest _changeset5 = create(:changeset, :user => user, :created_at => 3.months.ago.beginning_of_day, :num_changes => 5) changeset11 = create(:changeset, :user => user, :created_at => 3.months.ago.beginning_of_day, :num_changes => 11) - get user_path(user) + get user_heatmap_path(user) assert_response :success assert_select ".heatmap a", 2 @@ -431,7 +432,7 @@ class UsersControllerTest < ActionDispatch::IntegrationTest def test_heatmap_headline_changset_zero user = create(:user) - get user_path(user) + get user_heatmap_path(user) assert_response :success assert_select "h2.text-body-secondary.fs-5", :count => 0 @@ -441,7 +442,7 @@ class UsersControllerTest < ActionDispatch::IntegrationTest user = create(:user) create(:changeset, :user => user, :created_at => 4.months.ago.beginning_of_day, :num_changes => 1) - get user_path(user) + get user_heatmap_path(user) assert_response :success assert_select "h2.text-body-secondary.fs-5", :text => "1 contribution in the last year" @@ -451,7 +452,7 @@ class UsersControllerTest < ActionDispatch::IntegrationTest user = create(:user) create(:changeset, :user => user, :created_at => 4.months.ago.beginning_of_day, :num_changes => 12) - get user_path(user) + get user_heatmap_path(user) assert_response :success assert_select "h2.text-body-secondary.fs-5", :text => "12 contributions in the last year" -- 2.39.5