@@ -13,7 +13,15 @@ def index
1313 if project_scope
1414 # Single project performance view
1515 @timeframe = params [ :timeframe ] || 'hour'
16- @hours_back = ( params [ :hours_back ] || 24 ) . to_i
16+ # Adaptive default window so the page shows data by default
17+ requested_hours = ( params [ :hours_back ] || 24 ) . to_i
18+ @hours_back = requested_hours
19+ unless PerformanceEvent . where ( project : project_scope ) . where ( 'occurred_at > ?' , @hours_back . hours . ago ) . exists?
20+ @hours_back = [ @hours_back , 168 ] . max # 7 days
21+ end
22+ unless PerformanceEvent . where ( project : project_scope ) . where ( 'occurred_at > ?' , @hours_back . hours . ago ) . exists?
23+ @hours_back = [ @hours_back , 720 ] . max # 30 days
24+ end
1725
1826 @rollups = project_scope . perf_rollups
1927 . where ( timeframe : @timeframe )
@@ -34,14 +42,14 @@ def index
3442 . limit ( 20 )
3543
3644 # Calculate project-specific metrics
37- recent_rollups = project_scope . perf_rollups . where ( 'timestamp > ?' , 24 . hours . ago )
38- raw_events_scope = PerformanceEvent . where ( project : project_scope ) . where ( 'occurred_at > ?' , 24 . hours . ago )
45+ recent_rollups = project_scope . perf_rollups . where ( 'timestamp > ?' , @hours_back . hours . ago )
46+ raw_events_scope = PerformanceEvent . where ( project : project_scope ) . where ( 'occurred_at > ?' , @hours_back . hours . ago )
3947 slow_threshold_ms = 1000
4048
4149 if recent_rollups . exists?
42- total_requests = recent_rollups . sum ( :request_count )
43- total_errors = recent_rollups . sum ( :error_count )
44- avg_response = recent_rollups . average ( :avg_duration_ms )
50+ total_requests = recent_rollups . sum ( :request_count )
51+ total_errors = recent_rollups . sum ( :error_count )
52+ avg_response = recent_rollups . average ( :avg_duration_ms )
4553 slow_requests = raw_events_scope . where ( 'duration_ms > ?' , slow_threshold_ms ) . count
4654
4755 @metrics = {
@@ -56,9 +64,9 @@ def index
5664 avg_response = raw_events_scope . average ( :duration_ms )
5765 slow_requests = raw_events_scope . where ( 'duration_ms > ?' , slow_threshold_ms ) . count
5866
59- @metrics = {
60- response_time : avg_response ? "#{ avg_response . round ( 1 ) } ms" : "N/A" ,
61- throughput : "#{ total_requests } /day" ,
67+ @metrics = {
68+ response_time : avg_response ? "#{ avg_response . round ( 1 ) } ms" : "N/A" ,
69+ throughput : "#{ total_requests } /day" ,
6270 error_rate : total_requests > 0 ? "0.0%" : "0%" ,
6371 slow_requests : slow_requests
6472 }
@@ -192,9 +200,9 @@ def index
192200 @projects . each do |project |
193201 recent_rollups = project . perf_rollups . where ( 'timestamp > ?' , 24 . hours . ago )
194202 if recent_rollups . exists?
195- avg_response = recent_rollups . average ( :avg_duration_ms )
196- requests = recent_rollups . sum ( :request_count )
197- errors = recent_rollups . sum ( :error_count )
203+ avg_response = recent_rollups . average ( :avg_duration_ms )
204+ requests = recent_rollups . sum ( :request_count )
205+ errors = recent_rollups . sum ( :error_count )
198206 p95 = recent_rollups . average ( :p95_duration_ms )
199207 else
200208 raw_events = PerformanceEvent . where ( project : project ) . where ( 'occurred_at > ?' , 24 . hours . ago )
@@ -332,21 +340,21 @@ def action_detail
332340 @rollups = synthetic . sort_by ( &:timestamp )
333341 else
334342 # Calculate detailed metrics from rollups
335- @total_requests = @rollups . sum ( :request_count )
336- @total_errors = @rollups . sum ( :error_count )
337- @avg_response_time = @rollups . average ( :avg_duration_ms )
338- @p50_response_time = @rollups . average ( :p50_duration_ms )
339- @p95_response_time = @rollups . average ( :p95_duration_ms )
340- @p99_response_time = @rollups . average ( :p99_duration_ms )
341- @min_response_time = @rollups . minimum ( :min_duration_ms )
342- @max_response_time = @rollups . maximum ( :max_duration_ms )
343- @error_rate = @total_requests > 0 ? ( ( @total_errors . to_f / @total_requests ) * 100 ) . round ( 2 ) : 0
344-
345- # Group by timeframe for charts
346- @hourly_data = @rollups . where ( 'timestamp > ?' , 24 . hours . ago )
347- . group_by { |r | r . timestamp . beginning_of_hour }
348-
349- @daily_data = @rollups . group_by { |r | r . timestamp . beginning_of_day }
343+ @total_requests = @rollups . sum ( :request_count )
344+ @total_errors = @rollups . sum ( :error_count )
345+ @avg_response_time = @rollups . average ( :avg_duration_ms )
346+ @p50_response_time = @rollups . average ( :p50_duration_ms )
347+ @p95_response_time = @rollups . average ( :p95_duration_ms )
348+ @p99_response_time = @rollups . average ( :p99_duration_ms )
349+ @min_response_time = @rollups . minimum ( :min_duration_ms )
350+ @max_response_time = @rollups . maximum ( :max_duration_ms )
351+ @error_rate = @total_requests > 0 ? ( ( @total_errors . to_f / @total_requests ) * 100 ) . round ( 2 ) : 0
352+
353+ # Group by timeframe for charts
354+ @hourly_data = @rollups . where ( 'timestamp > ?' , 24 . hours . ago )
355+ . group_by { |r | r . timestamp . beginning_of_hour }
356+
357+ @daily_data = @rollups . group_by { |r | r . timestamp . beginning_of_day }
350358 end
351359
352360
@@ -363,7 +371,7 @@ def action_detail
363371
364372 # Graph data for this action
365373 if @current_tab == 'graph'
366- range_key = ( params [ :range ] || '24H ' ) . to_s . upcase
374+ range_key = ( params [ :range ] || '7D ' ) . to_s . upcase
367375 window_seconds = case range_key
368376 when '1H' then 1 . hour
369377 when '4H' then 4 . hours
@@ -391,20 +399,29 @@ def action_detail
391399 counts = Array . new ( bucket_count , 0 )
392400 labels = Array . new ( bucket_count ) { |i | start_time + i * bucket_seconds }
393401
394- event_times = PerformanceEvent . where ( project : project_scope , target : @target )
395- . where ( 'occurred_at >= ? AND occurred_at <= ?' , start_time , end_time )
396- . pluck ( :occurred_at )
397- event_times . each do |ts |
402+ events = PerformanceEvent . where ( project : project_scope , target : @target )
403+ . where ( 'occurred_at >= ? AND occurred_at <= ?' , start_time , end_time )
404+ . pluck ( :occurred_at , :duration_ms )
405+ sum_per_bucket = Array . new ( bucket_count , 0.0 )
406+ count_per_bucket = Array . new ( bucket_count , 0 )
407+ events . each do |ts , dur |
398408 idx = ( ( ( ts - start_time ) / bucket_seconds ) . floor ) . to_i
399409 next if idx . negative? || idx >= bucket_count
400410 counts [ idx ] += 1
411+ if dur
412+ sum_per_bucket [ idx ] += dur . to_f
413+ count_per_bucket [ idx ] += 1
414+ end
401415 end
402416
403417 @graph_labels = labels
404418 @graph_counts = counts
405419 @graph_max = [ counts . max || 0 , 1 ] . max
406420 @graph_has_data = counts . sum > 0
407421 @graph_range_key = range_key
422+
423+ # Additional series: average response time per bucket (ms)
424+ @graph_avg_ms = sum_per_bucket . each_with_index . map { |s , i | count_per_bucket [ i ] > 0 ? ( s / count_per_bucket [ i ] ) . round ( 1 ) : 0 }
408425 end
409426
410427 # AI summary generation on demand
0 commit comments