diff --git a/app/controllers/stats_controller.rb b/app/controllers/stats_controller.rb index 89043d5a..4dc37198 100644 --- a/app/controllers/stats_controller.rb +++ b/app/controllers/stats_controller.rb @@ -124,7 +124,44 @@ def index # Get daily and all-time stats @daily_stats = StatsService.daily_stats - @all_time_stats = StatsService.all_time_daily_stats + full_series = StatsService.all_time_daily_stats + + @range = params[:range].to_s + @range = "all" unless ["90", "365", "all"].include?(@range) + + series_for_range = case @range + when "90" + full_series.last(90) + when "365" + full_series.last(365) + else + full_series + end + + left_margin = 8 + right_margin = 2 + available_width = 100 - left_margin - right_margin + min_bar_width = 0.15 + bar_spacing = 0.05 + max_bars = (available_width / (min_bar_width + bar_spacing)).floor + + if series_for_range.length <= max_bars + @chart_stats = series_for_range + else + bin_size = (series_for_range.length.to_f / max_bars).ceil + @chart_stats = [] + + series_for_range.each_slice(bin_size) do |bin| + if bin.length == 1 + @chart_stats << bin.first + else + total_count = bin.sum(&:count) + last_date = bin.last.date + @chart_stats << OpenStruct.new(date: last_date, count: total_count) + end + end + end + @all_time_stats = full_series # Get top posters for different time periods @top_posters_today = StatsService.top_posters_today diff --git a/app/views/stats/index.html.erb b/app/views/stats/index.html.erb index a7f3b73d..96f2a51a 100644 --- a/app/views/stats/index.html.erb +++ b/app/views/stats/index.html.erb @@ -542,25 +542,30 @@
+
+ <%= form_with url: stats_path, method: :get, local: true do |f| %> + + + <% end %> +
<% - # Use all-time stats - max_count = @all_time_stats.map(&:count).max - total_days = @all_time_stats.length + max_count = @chart_stats.map(&:count).max + total_days = @chart_stats.length - # Fixed dimensions graph_height = 240 left_margin = 8 right_margin = 2 bottom_margin = 25 - # Calculate bar width and spacing available_width = 100 - left_margin - right_margin min_bar_width = 0.15 bar_spacing = 0.05 bar_width = [(available_width - (total_days * bar_spacing)) / total_days, min_bar_width].max - - # Show fewer labels for readability label_interval = (total_days / 6.0).ceil %> @@ -632,7 +637,7 @@ /> - <% @all_time_stats.each_with_index do |stat, index| %> + <% @chart_stats.each_with_index do |stat, index| %> <% bar_height = (stat.count.to_f / max_count * (graph_height - bottom_margin - 10)).round x_pos = left_margin + (index * (bar_width + bar_spacing)) diff --git a/test/controllers/stats_controller_test.rb b/test/controllers/stats_controller_test.rb new file mode 100644 index 00000000..f74858ee --- /dev/null +++ b/test/controllers/stats_controller_test.rb @@ -0,0 +1,40 @@ +require "test_helper" + +class StatsControllerTest < ActionDispatch::IntegrationTest + setup do + @user = users(:david) + post "/session", params: { email: @user.email_address, password: "secret123456" } + end + + test "index with default range renders successfully" do + get "/stats" + assert_response :success + assert_select "select[name='range'] option[value='all'][selected]" + end + + test "index with range=90 renders successfully" do + get "/stats", params: { range: "90" } + assert_response :success + assert_select "select[name='range'] option[value='90'][selected]" + end + + test "index with range=365 renders successfully" do + get "/stats", params: { range: "365" } + assert_response :success + assert_select "select[name='range'] option[value='365'][selected]" + end + + test "index with invalid range defaults to all" do + get "/stats", params: { range: "invalid" } + assert_response :success + assert_select "select[name='range'] option[value='all'][selected]" + end + + test "chart_stats is set correctly for different ranges" do + get "/stats", params: { range: "90" } + assert_response :success + assert_not_nil assigns(:chart_stats) + assert_not_nil assigns(:range) + assert_equal "90", assigns(:range) + end +end