Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion app/controllers/stats_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 13 additions & 8 deletions app/views/stats/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -542,25 +542,30 @@

<!-- Graph visualization -->
<div class="margin-top">
<div class="flex justify-content-end" style="margin-bottom: 8px; gap: 6px;">
<%= form_with url: stats_path, method: :get, local: true do |f| %>
<label class="txt-small txt-faded" for="range">Range:</label>
<select name="range" id="range" onchange="this.form.submit()">
<option value="90" <%= 'selected' if @range == '90' %>>Last 90</option>
<option value="365" <%= 'selected' if @range == '365' %>>Last 365</option>
<option value="all" <%= 'selected' if @range == 'all' %>>All</option>
</select>
<% end %>
</div>
<div style="height: 300px;">
<%
# 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
%>

Expand Down Expand Up @@ -632,7 +637,7 @@
/>

<!-- Bars -->
<% @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))
Expand Down
40 changes: 40 additions & 0 deletions test/controllers/stats_controller_test.rb
Original file line number Diff line number Diff line change
@@ -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