mirror of
https://github.com/TracksApp/tracks.git
synced 2025-12-16 15:20:13 +01:00
Merge pull request #2231 from ZeiP/bug/1153_stats
Change the Flash charts to Charts.js
This commit is contained in:
commit
6ad1e2cf93
30 changed files with 552 additions and 1113 deletions
3
Gemfile
3
Gemfile
|
|
@ -31,7 +31,6 @@ gem "will_paginate"
|
||||||
gem "acts_as_list"
|
gem "acts_as_list"
|
||||||
gem "aasm", '~> 3.4.0'
|
gem "aasm", '~> 3.4.0'
|
||||||
gem "htmlentities"
|
gem "htmlentities"
|
||||||
gem "swf_fu"
|
|
||||||
gem "rails_autolink"
|
gem "rails_autolink"
|
||||||
gem 'puma', '~> 3.12'
|
gem 'puma', '~> 3.12'
|
||||||
gem 'paperclip'
|
gem 'paperclip'
|
||||||
|
|
@ -39,6 +38,8 @@ gem 'paperclip'
|
||||||
# To use ActiveModel has_secure_password
|
# To use ActiveModel has_secure_password
|
||||||
gem 'bcrypt', '~> 3.1.7'
|
gem 'bcrypt', '~> 3.1.7'
|
||||||
|
|
||||||
|
gem 'chartjs-ror'
|
||||||
|
|
||||||
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
|
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
|
||||||
# gem 'turbolinks'
|
# gem 'turbolinks'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,8 @@ GEM
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
uniform_notifier (~> 1.11)
|
uniform_notifier (~> 1.11)
|
||||||
byebug (11.0.1)
|
byebug (11.0.1)
|
||||||
|
chartjs-ror (3.6.4)
|
||||||
|
rails (>= 3.1)
|
||||||
childprocess (1.0.1)
|
childprocess (1.0.1)
|
||||||
rake (< 13.0)
|
rake (< 13.0)
|
||||||
climate_control (0.2.0)
|
climate_control (0.2.0)
|
||||||
|
|
@ -238,9 +240,6 @@ GEM
|
||||||
activesupport (>= 4.0)
|
activesupport (>= 4.0)
|
||||||
sprockets (>= 3.0.0)
|
sprockets (>= 3.0.0)
|
||||||
sqlite3 (1.4.1)
|
sqlite3 (1.4.1)
|
||||||
swf_fu (2.0.4)
|
|
||||||
coffee-script
|
|
||||||
rails (>= 3.1)
|
|
||||||
terrapin (0.6.0)
|
terrapin (0.6.0)
|
||||||
climate_control (>= 0.0.3, < 1.0)
|
climate_control (>= 0.0.3, < 1.0)
|
||||||
therubyracer (0.12.3)
|
therubyracer (0.12.3)
|
||||||
|
|
@ -278,6 +277,7 @@ DEPENDENCIES
|
||||||
bootstrap-sass (= 3.4.1)
|
bootstrap-sass (= 3.4.1)
|
||||||
bullet
|
bullet
|
||||||
byebug
|
byebug
|
||||||
|
chartjs-ror
|
||||||
codeclimate-test-reporter (= 1.0.7)
|
codeclimate-test-reporter (= 1.0.7)
|
||||||
coffee-rails (~> 4.2.0)
|
coffee-rails (~> 4.2.0)
|
||||||
database_cleaner
|
database_cleaner
|
||||||
|
|
@ -305,7 +305,6 @@ DEPENDENCIES
|
||||||
simplecov
|
simplecov
|
||||||
spring
|
spring
|
||||||
sqlite3
|
sqlite3
|
||||||
swf_fu
|
|
||||||
therubyracer
|
therubyracer
|
||||||
tolk (~> 3.1.0)
|
tolk (~> 3.1.0)
|
||||||
uglifier (>= 1.3.0)
|
uglifier (>= 1.3.0)
|
||||||
|
|
|
||||||
|
|
@ -36,3 +36,6 @@
|
||||||
//= require jquery.truncator
|
//= require jquery.truncator
|
||||||
//= require superfish
|
//= require superfish
|
||||||
//= require supersubs
|
//= require supersubs
|
||||||
|
|
||||||
|
//= require Chart.min
|
||||||
|
//= require chartjs-plugin-colorschemes.min
|
||||||
|
|
|
||||||
11
app/assets/javascripts/chartjs-plugin-colorschemes.min.js
vendored
Normal file
11
app/assets/javascripts/chartjs-plugin-colorschemes.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1266,7 +1266,7 @@ button.positive, .widgets a.positive{
|
||||||
background-color:black;
|
background-color:black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats_content .open-flash-chart, .stats_content .stats_module {
|
.stats_content .chart, .stats_content .stats_module {
|
||||||
float: left;
|
float: left;
|
||||||
width: 450px;
|
width: 450px;
|
||||||
margin-right:20px;
|
margin-right:20px;
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -11,42 +11,6 @@ class StatsController < ApplicationController
|
||||||
@stats = Stats::UserStats.new(current_user)
|
@stats = Stats::UserStats.new(current_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def actions_done_last12months_data
|
|
||||||
# get actions created and completed in the past 12+3 months. +3 for running
|
|
||||||
# - outermost set of entries needed for these calculations
|
|
||||||
actions_last12months = current_user.todos.created_or_completed_after(@cut_off_year_plus3).select("completed_at,created_at")
|
|
||||||
|
|
||||||
# convert to array and fill in non-existing months
|
|
||||||
@actions_done_last12months_array = put_events_into_month_buckets(actions_last12months, 13, :completed_at)
|
|
||||||
@actions_created_last12months_array = put_events_into_month_buckets(actions_last12months, 13, :created_at)
|
|
||||||
|
|
||||||
# find max for graph in both arrays
|
|
||||||
@max = (@actions_done_last12months_array + @actions_created_last12months_array).max
|
|
||||||
|
|
||||||
# find running avg
|
|
||||||
done_in_last_15_months = put_events_into_month_buckets(actions_last12months, 16, :completed_at)
|
|
||||||
created_in_last_15_months = put_events_into_month_buckets(actions_last12months, 16, :created_at)
|
|
||||||
|
|
||||||
@actions_done_avg_last12months_array = compute_running_avg_array(done_in_last_15_months, 13)
|
|
||||||
@actions_created_avg_last12months_array = compute_running_avg_array(created_in_last_15_months, 13)
|
|
||||||
|
|
||||||
# interpolate avg for current month.
|
|
||||||
@interpolated_actions_created_this_month = interpolate_avg_for_current_month(@actions_created_last12months_array)
|
|
||||||
@interpolated_actions_done_this_month = interpolate_avg_for_current_month(@actions_done_last12months_array)
|
|
||||||
|
|
||||||
@created_count_array = Array.new(13, actions_last12months.created_after(@cut_off_year).count(:all)/12.0)
|
|
||||||
@done_count_array = Array.new(13, actions_last12months.completed_after(@cut_off_year).count(:all)/12.0)
|
|
||||||
render :layout => false
|
|
||||||
end
|
|
||||||
|
|
||||||
def interpolate_avg_for_current_month(set)
|
|
||||||
(set[0]*(1/percent_of_month) + set[1] + set[2]) / 3.0
|
|
||||||
end
|
|
||||||
|
|
||||||
def percent_of_month
|
|
||||||
Time.zone.now.day / Time.zone.now.end_of_month.day.to_f
|
|
||||||
end
|
|
||||||
|
|
||||||
def actions_done_last_years
|
def actions_done_last_years
|
||||||
@page_title = t('stats.index_title')
|
@page_title = t('stats.index_title')
|
||||||
@chart = Stats::Chart.new('actions_done_lastyears_data', :height => 400, :width => 900)
|
@chart = Stats::Chart.new('actions_done_lastyears_data', :height => 400, :width => 900)
|
||||||
|
|
@ -80,191 +44,6 @@ class StatsController < ApplicationController
|
||||||
render :layout => false
|
render :layout => false
|
||||||
end
|
end
|
||||||
|
|
||||||
def actions_done_last30days_data
|
|
||||||
# get actions created and completed in the past 30 days.
|
|
||||||
@actions_done_last30days = current_user.todos.completed_after(@cut_off_30days).select("completed_at")
|
|
||||||
@actions_created_last30days = current_user.todos.created_after(@cut_off_30days).select("created_at")
|
|
||||||
|
|
||||||
# convert to array. 30+1 to have 30 complete days and one current day [0]
|
|
||||||
@actions_done_last30days_array = convert_to_days_from_today_array(@actions_done_last30days, 31, :completed_at)
|
|
||||||
@actions_created_last30days_array = convert_to_days_from_today_array(@actions_created_last30days, 31, :created_at)
|
|
||||||
|
|
||||||
# find max for graph in both hashes
|
|
||||||
@max = [@actions_done_last30days_array.max, @actions_created_last30days_array.max].max
|
|
||||||
|
|
||||||
render :layout => false
|
|
||||||
end
|
|
||||||
|
|
||||||
def actions_completion_time_data
|
|
||||||
@actions_completion_time = current_user.todos.completed.select("completed_at, created_at").reorder("completed_at DESC" )
|
|
||||||
|
|
||||||
# convert to array and fill in non-existing weeks with 0
|
|
||||||
@max_weeks = @actions_completion_time.last ? difference_in_weeks(@today, @actions_completion_time.last.completed_at) : 1
|
|
||||||
@actions_completed_per_week_array = convert_to_weeks_running_array(@actions_completion_time, @max_weeks+1)
|
|
||||||
|
|
||||||
# stop the chart after 10 weeks
|
|
||||||
@count = [10, @max_weeks].min
|
|
||||||
|
|
||||||
# convert to new array to hold max @cut_off elems + 1 for sum of actions after @cut_off
|
|
||||||
@actions_completion_time_array = cut_off_array_with_sum(@actions_completed_per_week_array, @count)
|
|
||||||
@max_actions = @actions_completion_time_array.max
|
|
||||||
|
|
||||||
# get percentage done cumulative
|
|
||||||
@cum_percent_done = convert_to_cumulative_array(@actions_completion_time_array, @actions_completion_time.count(:all))
|
|
||||||
|
|
||||||
render :layout => false
|
|
||||||
end
|
|
||||||
|
|
||||||
def actions_running_time_data
|
|
||||||
@actions_running_time = current_user.todos.not_completed.select("created_at").reorder("created_at DESC")
|
|
||||||
|
|
||||||
# convert to array and fill in non-existing weeks with 0
|
|
||||||
@max_weeks = difference_in_weeks(@today, @actions_running_time.last.created_at)
|
|
||||||
@actions_running_per_week_array = convert_to_weeks_from_today_array(@actions_running_time, @max_weeks+1, :created_at)
|
|
||||||
|
|
||||||
# cut off chart at 52 weeks = one year
|
|
||||||
@count = [52, @max_weeks].min
|
|
||||||
|
|
||||||
# convert to new array to hold max @cut_off elems + 1 for sum of actions after @cut_off
|
|
||||||
@actions_running_time_array = cut_off_array_with_sum(@actions_running_per_week_array, @count)
|
|
||||||
@max_actions = @actions_running_time_array.max
|
|
||||||
|
|
||||||
# get percentage done cumulative
|
|
||||||
@cum_percent_done = convert_to_cumulative_array(@actions_running_time_array, @actions_running_time.count )
|
|
||||||
|
|
||||||
render :layout => false
|
|
||||||
end
|
|
||||||
|
|
||||||
def actions_visible_running_time_data
|
|
||||||
# running means
|
|
||||||
# - not completed (completed_at must be null)
|
|
||||||
# visible means
|
|
||||||
# - actions not part of a hidden project
|
|
||||||
# - actions not part of a hidden context
|
|
||||||
# - actions not deferred (show_from must be null)
|
|
||||||
# - actions not pending/blocked
|
|
||||||
|
|
||||||
@actions_running_time = current_user.todos.not_completed.not_hidden.not_deferred_or_blocked.
|
|
||||||
select("todos.created_at").
|
|
||||||
reorder("todos.created_at DESC")
|
|
||||||
|
|
||||||
@max_weeks = difference_in_weeks(@today, @actions_running_time.last.created_at)
|
|
||||||
@actions_running_per_week_array = convert_to_weeks_from_today_array(@actions_running_time, @max_weeks+1, :created_at)
|
|
||||||
|
|
||||||
# cut off chart at 52 weeks = one year
|
|
||||||
@count = [52, @max_weeks].min
|
|
||||||
|
|
||||||
# convert to new array to hold max @cut_off elems + 1 for sum of actions after @cut_off
|
|
||||||
@actions_running_time_array = cut_off_array_with_sum(@actions_running_per_week_array, @count)
|
|
||||||
@max_actions = @actions_running_time_array.max
|
|
||||||
|
|
||||||
# get percentage done cumulative
|
|
||||||
@cum_percent_done = convert_to_cumulative_array(@actions_running_time_array, @actions_running_time.count )
|
|
||||||
|
|
||||||
render :layout => false
|
|
||||||
end
|
|
||||||
|
|
||||||
def actions_open_per_week_data
|
|
||||||
@actions_started = current_user.todos.created_after(@today-53.weeks).
|
|
||||||
select("todos.created_at, todos.completed_at").
|
|
||||||
reorder("todos.created_at DESC")
|
|
||||||
|
|
||||||
@max_weeks = difference_in_weeks(@today, @actions_started.last.created_at)
|
|
||||||
|
|
||||||
# cut off chart at 52 weeks = one year
|
|
||||||
@count = [52, @max_weeks].min
|
|
||||||
|
|
||||||
@actions_open_per_week_array = convert_to_weeks_running_from_today_array(@actions_started, @max_weeks+1)
|
|
||||||
@actions_open_per_week_array = cut_off_array(@actions_open_per_week_array, @count)
|
|
||||||
@max_actions = (@actions_open_per_week_array.max or 0)
|
|
||||||
|
|
||||||
render :layout => false
|
|
||||||
end
|
|
||||||
|
|
||||||
def context_total_actions_data
|
|
||||||
actions = Stats::TopContextsQuery.new(current_user).result
|
|
||||||
|
|
||||||
@data = Stats::PieChartData.new(actions, t('stats.spread_of_actions_for_all_context'), 70)
|
|
||||||
|
|
||||||
render :pie_chart_data, :layout => false
|
|
||||||
end
|
|
||||||
|
|
||||||
def context_running_actions_data
|
|
||||||
actions = Stats::TopContextsQuery.new(current_user, :running => true).result
|
|
||||||
@data = Stats::PieChartData.new(actions, t('stats.spread_of_running_actions_for_visible_contexts'), 60)
|
|
||||||
|
|
||||||
render :pie_chart_data, :layout => false
|
|
||||||
end
|
|
||||||
|
|
||||||
def actions_day_of_week_all_data
|
|
||||||
@actions_creation_day = current_user.todos.select("created_at")
|
|
||||||
@actions_completion_day = current_user.todos.completed.select("completed_at")
|
|
||||||
|
|
||||||
# convert to array and fill in non-existing days
|
|
||||||
@actions_creation_day_array = Array.new(7) { |i| 0}
|
|
||||||
@actions_creation_day.each { |t| @actions_creation_day_array[ t.created_at.wday ] += 1 }
|
|
||||||
@max = @actions_creation_day_array.max
|
|
||||||
|
|
||||||
# convert to array and fill in non-existing days
|
|
||||||
@actions_completion_day_array = Array.new(7) { |i| 0}
|
|
||||||
@actions_completion_day.each { |t| @actions_completion_day_array[ t.completed_at.wday ] += 1 }
|
|
||||||
@max = @actions_completion_day_array.max
|
|
||||||
|
|
||||||
render :layout => false
|
|
||||||
end
|
|
||||||
|
|
||||||
def actions_day_of_week_30days_data
|
|
||||||
@actions_creation_day = current_user.todos.created_after(@cut_off_month).select("created_at")
|
|
||||||
@actions_completion_day = current_user.todos.completed_after(@cut_off_month).select("completed_at")
|
|
||||||
|
|
||||||
# convert to hash to be able to fill in non-existing days
|
|
||||||
@max=0
|
|
||||||
@actions_creation_day_array = Array.new(7) { |i| 0}
|
|
||||||
@actions_creation_day.each { |r| @actions_creation_day_array[ r.created_at.wday ] += 1 }
|
|
||||||
|
|
||||||
# convert to hash to be able to fill in non-existing days
|
|
||||||
@actions_completion_day_array = Array.new(7) { |i| 0}
|
|
||||||
@actions_completion_day.each { |r| @actions_completion_day_array[r.completed_at.wday] += 1 }
|
|
||||||
|
|
||||||
@max = [@actions_creation_day_array.max, @actions_completion_day_array.max].max
|
|
||||||
|
|
||||||
render :layout => false
|
|
||||||
end
|
|
||||||
|
|
||||||
def actions_time_of_day_all_data
|
|
||||||
@actions_creation_hour = current_user.todos.select("created_at")
|
|
||||||
@actions_completion_hour = current_user.todos.completed.select("completed_at")
|
|
||||||
|
|
||||||
# convert to hash to be able to fill in non-existing days
|
|
||||||
@actions_creation_hour_array = Array.new(24) { |i| 0}
|
|
||||||
@actions_creation_hour.each{|r| @actions_creation_hour_array[r.created_at.hour] += 1 }
|
|
||||||
|
|
||||||
# convert to hash to be able to fill in non-existing days
|
|
||||||
@actions_completion_hour_array = Array.new(24) { |i| 0}
|
|
||||||
@actions_completion_hour.each{|r| @actions_completion_hour_array[r.completed_at.hour] += 1 }
|
|
||||||
|
|
||||||
@max = [@actions_creation_hour_array.max, @actions_completion_hour_array.max].max
|
|
||||||
|
|
||||||
render :layout => false
|
|
||||||
end
|
|
||||||
|
|
||||||
def actions_time_of_day_30days_data
|
|
||||||
@actions_creation_hour = current_user.todos.created_after(@cut_off_month).select("created_at")
|
|
||||||
@actions_completion_hour = current_user.todos.completed_after(@cut_off_month).select("completed_at")
|
|
||||||
|
|
||||||
# convert to hash to be able to fill in non-existing days
|
|
||||||
@actions_creation_hour_array = Array.new(24) { |i| 0}
|
|
||||||
@actions_creation_hour.each{|r| @actions_creation_hour_array[r.created_at.hour] += 1 }
|
|
||||||
|
|
||||||
# convert to hash to be able to fill in non-existing days
|
|
||||||
@actions_completion_hour_array = Array.new(24) { |i| 0}
|
|
||||||
@actions_completion_hour.each{|r| @actions_completion_hour_array[r.completed_at.hour] += 1 }
|
|
||||||
|
|
||||||
@max = [@actions_creation_hour_array.max, @max = @actions_completion_hour_array.max].max
|
|
||||||
|
|
||||||
render :layout => false
|
|
||||||
end
|
|
||||||
|
|
||||||
def show_selected_actions_from_chart
|
def show_selected_actions_from_chart
|
||||||
@page_title = t('stats.action_selection_title')
|
@page_title = t('stats.action_selection_title')
|
||||||
@count = 99
|
@count = 99
|
||||||
|
|
@ -385,58 +164,6 @@ class StatsController < ApplicationController
|
||||||
convert_to_array(records.select { |x| x.send(date_method_on_todo) }, array_size) { |r| [difference_in_months(@today, r.send(date_method_on_todo))]}
|
convert_to_array(records.select { |x| x.send(date_method_on_todo) }, array_size) { |r| [difference_in_months(@today, r.send(date_method_on_todo))]}
|
||||||
end
|
end
|
||||||
|
|
||||||
def convert_to_days_from_today_array(records, array_size, date_method_on_todo)
|
|
||||||
return convert_to_array(records, array_size){ |r| [difference_in_days(@today, r.send(date_method_on_todo))]}
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_to_weeks_from_today_array(records, array_size, date_method_on_todo)
|
|
||||||
return convert_to_array(records, array_size) { |r| [difference_in_weeks(@today, r.send(date_method_on_todo))]}
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_to_weeks_running_array(records, array_size)
|
|
||||||
return convert_to_array(records, array_size) { |r| [difference_in_weeks(r.completed_at, r.created_at)]}
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_to_weeks_running_from_today_array(records, array_size)
|
|
||||||
return convert_to_array(records, array_size) { |r| week_indexes_of(r) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def week_indexes_of(record)
|
|
||||||
a = []
|
|
||||||
start_week = difference_in_weeks(@today, record.created_at)
|
|
||||||
end_week = record.completed_at ? difference_in_weeks(@today, record.completed_at) : 0
|
|
||||||
end_week.upto(start_week) { |i| a << i };
|
|
||||||
return a
|
|
||||||
end
|
|
||||||
|
|
||||||
# returns a new array containing all elems of array up to cut_off and
|
|
||||||
# adds the sum of the rest of array to the last elem
|
|
||||||
def cut_off_array_with_sum(array, cut_off)
|
|
||||||
# +1 to hold sum of rest
|
|
||||||
a = Array.new(cut_off+1){|i| array[i]||0}
|
|
||||||
# add rest of array to last elem
|
|
||||||
a[cut_off] += array.inject(:+) - a.inject(:+)
|
|
||||||
return a
|
|
||||||
end
|
|
||||||
|
|
||||||
def cut_off_array(array, cut_off)
|
|
||||||
return Array.new(cut_off){|i| array[i]||0}
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_to_cumulative_array(array, max)
|
|
||||||
# calculate fractions
|
|
||||||
a = Array.new(array.size){|i| array[i]*100.0/max}
|
|
||||||
# make cumulative
|
|
||||||
1.upto(array.size-1){ |i| a[i] += a[i-1] }
|
|
||||||
return a
|
|
||||||
end
|
|
||||||
|
|
||||||
# assumes date1 > date2
|
|
||||||
# this results in the number of months before the month of date1, not taking days into account, so diff of 31-dec and 1-jan is 1 month!
|
|
||||||
def difference_in_months(date1, date2)
|
|
||||||
return (date1.utc.year - date2.utc.year)*12 + (date1.utc.month - date2.utc.month)
|
|
||||||
end
|
|
||||||
|
|
||||||
# assumes date1 > date2
|
# assumes date1 > date2
|
||||||
def difference_in_days(date1, date2)
|
def difference_in_days(date1, date2)
|
||||||
return ((date1.utc.at_midnight-date2.utc.at_midnight)/SECONDS_PER_DAY).to_i
|
return ((date1.utc.at_midnight-date2.utc.at_midnight)/SECONDS_PER_DAY).to_i
|
||||||
|
|
@ -446,22 +173,4 @@ class StatsController < ApplicationController
|
||||||
def difference_in_weeks(date1, date2)
|
def difference_in_weeks(date1, date2)
|
||||||
return difference_in_days(date1, date2) / 7
|
return difference_in_days(date1, date2) / 7
|
||||||
end
|
end
|
||||||
|
|
||||||
def three_month_avg(set, i)
|
|
||||||
(set.fetch(i){ 0 } + set.fetch(i+1){ 0 } + set.fetch(i+2){ 0 }) / 3.0
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_three_month_avg(set,upper_bound)
|
|
||||||
(0..upper_bound-1).map { |i| three_month_avg(set, i) }
|
|
||||||
end
|
|
||||||
|
|
||||||
# sets "null" on first column and - if necessary - cleans up last two columns, which may have insufficient data
|
|
||||||
def compute_running_avg_array(set, upper_bound)
|
|
||||||
result = set_three_month_avg(set, upper_bound)
|
|
||||||
result[upper_bound-1] = result[upper_bound-1] * 3 if upper_bound == set.length
|
|
||||||
result[upper_bound-2] = result[upper_bound-2] * 3 / 2 if upper_bound > 1 and upper_bound == set.length
|
|
||||||
result[0] = "null"
|
|
||||||
result
|
|
||||||
end # unsolved, not triggered, edge case for set.length == upper_bound + 1
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,12 @@ module Stats
|
||||||
attr_reader :user
|
attr_reader :user
|
||||||
def initialize(user)
|
def initialize(user)
|
||||||
@user = user
|
@user = user
|
||||||
|
|
||||||
|
@today = Time.zone.now.utc.beginning_of_day
|
||||||
|
@cut_off_year = 12.months.ago.beginning_of_day
|
||||||
|
@cut_off_year_plus3 = 15.months.ago.beginning_of_day
|
||||||
|
@cut_off_month = 1.month.ago.beginning_of_day
|
||||||
|
@cut_off_30days = 30.days.ago.beginning_of_day
|
||||||
end
|
end
|
||||||
|
|
||||||
def ttc
|
def ttc
|
||||||
|
|
@ -28,28 +34,281 @@ module Stats
|
||||||
@sum_actions_created_last12months ||= new_since(one_year)
|
@sum_actions_created_last12months ||= new_since(one_year)
|
||||||
end
|
end
|
||||||
|
|
||||||
def completion_charts
|
def done_last12months_data
|
||||||
@completion_charts ||= %w{
|
# get actions created and completed in the past 12+3 months. +3 for running
|
||||||
actions_done_last30days_data
|
# - outermost set of entries needed for these calculations
|
||||||
actions_done_last12months_data
|
actions_last12months = @user.todos.created_or_completed_after(@cut_off_year_plus3).select("completed_at,created_at")
|
||||||
actions_completion_time_data
|
|
||||||
}.map do |action|
|
# convert to array and fill in non-existing months
|
||||||
Stats::Chart.new(action)
|
@actions_done_last12months_array = put_events_into_month_buckets(actions_last12months, 13, :completed_at)
|
||||||
end
|
@actions_created_last12months_array = put_events_into_month_buckets(actions_last12months, 13, :created_at)
|
||||||
|
|
||||||
|
# find max for graph in both arrays
|
||||||
|
@max = (@actions_done_last12months_array + @actions_created_last12months_array).max
|
||||||
|
|
||||||
|
# find running avg
|
||||||
|
done_in_last_15_months = put_events_into_month_buckets(actions_last12months, 16, :completed_at)
|
||||||
|
created_in_last_15_months = put_events_into_month_buckets(actions_last12months, 16, :created_at)
|
||||||
|
|
||||||
|
@actions_done_avg_last12months_array = compute_running_avg_array(done_in_last_15_months, 13)
|
||||||
|
@actions_created_avg_last12months_array = compute_running_avg_array(created_in_last_15_months, 13)
|
||||||
|
|
||||||
|
# interpolate avg for current month.
|
||||||
|
# FIXME: These should also be used.
|
||||||
|
@interpolated_actions_created_this_month = interpolate_avg_for_current_month(@actions_created_last12months_array)
|
||||||
|
@interpolated_actions_done_this_month = interpolate_avg_for_current_month(@actions_done_last12months_array)
|
||||||
|
|
||||||
|
@created_count_array = Array.new(13, actions_last12months.created_after(@cut_off_year).count(:all)/12.0)
|
||||||
|
@done_count_array = Array.new(13, actions_last12months.completed_after(@cut_off_year).count(:all)/12.0)
|
||||||
|
|
||||||
|
return {
|
||||||
|
datasets: [
|
||||||
|
{label: I18n.t('stats.labels.avg_created'), data: @created_count_array, type: "line"},
|
||||||
|
{label: I18n.t('stats.labels.avg_completed'), data: @done_count_array, type: "line"},
|
||||||
|
{label: I18n.t('stats.labels.month_avg_completed', :months => 3), data: @actions_done_avg_last12months_array, type: "line"},
|
||||||
|
{label: I18n.t('stats.labels.month_avg_created', :months => 3), data: @actions_created_avg_last12months_array, type: "line"},
|
||||||
|
{label: I18n.t('stats.labels.created'), data: @actions_created_last12months_array},
|
||||||
|
{label: I18n.t('stats.labels.completed'), data: @actions_done_last12months_array},
|
||||||
|
],
|
||||||
|
labels: array_of_month_labels(@done_count_array.size),
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def timing_charts
|
def done_last30days_data
|
||||||
@timing_charts ||= %w{
|
# get actions created and completed in the past 30 days.
|
||||||
actions_visible_running_time_data
|
@actions_done_last30days = @user.todos.completed_after(@cut_off_30days).select("completed_at")
|
||||||
actions_running_time_data
|
@actions_created_last30days = @user.todos.created_after(@cut_off_30days).select("created_at")
|
||||||
actions_open_per_week_data
|
|
||||||
actions_day_of_week_all_data
|
# convert to array. 30+1 to have 30 complete days and one current day [0]
|
||||||
actions_day_of_week_30days_data
|
@actions_done_last30days_array = convert_to_days_from_today_array(@actions_done_last30days, 31, :completed_at)
|
||||||
actions_time_of_day_all_data
|
@actions_created_last30days_array = convert_to_days_from_today_array(@actions_created_last30days, 31, :created_at)
|
||||||
actions_time_of_day_30days_data
|
|
||||||
}.map do |action|
|
# find max for graph in both hashes
|
||||||
Stats::Chart.new(action)
|
@max = [@actions_done_last30days_array.max, @actions_created_last30days_array.max].max
|
||||||
end
|
|
||||||
|
created_count_array = Array.new(30){ |i| @actions_created_last30days.size/30.0 }
|
||||||
|
done_count_array = Array.new(30){ |i| @actions_done_last30days.size/30.0 }
|
||||||
|
# TODO: make the strftime i18n proof
|
||||||
|
time_labels = Array.new(30){ |i| I18n.l(Time.zone.now-i.days, :format => :stats) }
|
||||||
|
|
||||||
|
return {
|
||||||
|
datasets: [
|
||||||
|
{label: I18n.t('stats.labels.avg_created'), data: created_count_array, type: "line"},
|
||||||
|
{label: I18n.t('stats.labels.avg_completed'), data: done_count_array, type: "line"},
|
||||||
|
{label: I18n.t('stats.labels.created'), data: @actions_created_last30days_array},
|
||||||
|
{label: I18n.t('stats.labels.completed'), data: @actions_done_last30days_array},
|
||||||
|
],
|
||||||
|
labels: time_labels,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def completion_time_data
|
||||||
|
@actions_completion_time = @user.todos.completed.select("completed_at, created_at").reorder("completed_at DESC" )
|
||||||
|
|
||||||
|
# convert to array and fill in non-existing weeks with 0
|
||||||
|
@max_weeks = @actions_completion_time.last ? difference_in_weeks(@today, @actions_completion_time.last.completed_at) : 1
|
||||||
|
@actions_completed_per_week_array = convert_to_weeks_running_array(@actions_completion_time, @max_weeks+1)
|
||||||
|
|
||||||
|
# stop the chart after 10 weeks
|
||||||
|
@count = [10, @max_weeks].min
|
||||||
|
|
||||||
|
# convert to new array to hold max @cut_off elems + 1 for sum of actions after @cut_off
|
||||||
|
@actions_completion_time_array = cut_off_array_with_sum(@actions_completed_per_week_array, @count)
|
||||||
|
@max_actions = @actions_completion_time_array.max
|
||||||
|
|
||||||
|
# get percentage done cumulative
|
||||||
|
@cum_percent_done = convert_to_cumulative_array(@actions_completion_time_array, @actions_completion_time.count(:all))
|
||||||
|
|
||||||
|
time_labels = Array.new(@count){ |i| "#{i}-#{i+1}" }
|
||||||
|
time_labels[0] = I18n.t('stats.within_one')
|
||||||
|
time_labels[@count] = "> #{@count}"
|
||||||
|
|
||||||
|
return {
|
||||||
|
datasets: [
|
||||||
|
{label: I18n.t('stats.legend.percentage'), data: @cum_percent_done, type: "line"},
|
||||||
|
{label: I18n.t('stats.legend.actions'), data: @actions_completion_time_array},
|
||||||
|
],
|
||||||
|
labels: time_labels,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def running_time_data
|
||||||
|
@actions_running_time = @user.todos.not_completed.select("created_at").reorder("created_at DESC")
|
||||||
|
|
||||||
|
# convert to array and fill in non-existing weeks with 0
|
||||||
|
@max_weeks = difference_in_weeks(@today, @actions_running_time.last.created_at)
|
||||||
|
@actions_running_per_week_array = convert_to_weeks_from_today_array(@actions_running_time, @max_weeks+1, :created_at)
|
||||||
|
|
||||||
|
# cut off chart at 52 weeks = one year
|
||||||
|
@count = [52, @max_weeks].min
|
||||||
|
|
||||||
|
# convert to new array to hold max @cut_off elems + 1 for sum of actions after @cut_off
|
||||||
|
@actions_running_time_array = cut_off_array_with_sum(@actions_running_per_week_array, @count)
|
||||||
|
@max_actions = @actions_running_time_array.max
|
||||||
|
|
||||||
|
# get percentage done cumulative
|
||||||
|
@cum_percent_done = convert_to_cumulative_array(@actions_running_time_array, @actions_running_time.count )
|
||||||
|
|
||||||
|
time_labels = Array.new(@count){ |i| "#{i}-#{i+1}" }
|
||||||
|
time_labels[0] = "< 1"
|
||||||
|
time_labels[@count] = "> #{@count}"
|
||||||
|
|
||||||
|
return {
|
||||||
|
datasets: [
|
||||||
|
{label: I18n.t('stats.running_time_all_legend.percentage'), data: @cum_percent_done, type: "line"},
|
||||||
|
{label: I18n.t('stats.running_time_all_legend.actions'), data: @actions_running_time_array},
|
||||||
|
],
|
||||||
|
labels: time_labels,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def visible_running_time_data
|
||||||
|
# running means
|
||||||
|
# - not completed (completed_at must be null)
|
||||||
|
# visible means
|
||||||
|
# - actions not part of a hidden project
|
||||||
|
# - actions not part of a hidden context
|
||||||
|
# - actions not deferred (show_from must be null)
|
||||||
|
# - actions not pending/blocked
|
||||||
|
|
||||||
|
@actions_running_time = @user.todos.not_completed.not_hidden.not_deferred_or_blocked.
|
||||||
|
select("todos.created_at").
|
||||||
|
reorder("todos.created_at DESC")
|
||||||
|
|
||||||
|
@max_weeks = difference_in_weeks(@today, @actions_running_time.last.created_at)
|
||||||
|
@actions_running_per_week_array = convert_to_weeks_from_today_array(@actions_running_time, @max_weeks+1, :created_at)
|
||||||
|
|
||||||
|
# cut off chart at 52 weeks = one year
|
||||||
|
@count = [52, @max_weeks].min
|
||||||
|
|
||||||
|
# convert to new array to hold max @cut_off elems + 1 for sum of actions after @cut_off
|
||||||
|
@actions_running_time_array = cut_off_array_with_sum(@actions_running_per_week_array, @count)
|
||||||
|
@max_actions = @actions_running_time_array.max
|
||||||
|
|
||||||
|
# get percentage done cumulative
|
||||||
|
@cum_percent_done = convert_to_cumulative_array(@actions_running_time_array, @actions_running_time.count )
|
||||||
|
|
||||||
|
time_labels = Array.new(@count){ |i| "#{i}-#{i+1}" }
|
||||||
|
time_labels[0] = "< 1"
|
||||||
|
time_labels[@count] = "> #{@count}"
|
||||||
|
|
||||||
|
return {
|
||||||
|
datasets: [
|
||||||
|
{label: I18n.t('stats.running_time_legend.percentage'), data: @cum_percent_done, type: "line"},
|
||||||
|
{label: I18n.t('stats.running_time_legend.actions'), data: @actions_running_time_array},
|
||||||
|
],
|
||||||
|
labels: time_labels,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def open_per_week_data
|
||||||
|
@actions_started = @user.todos.created_after(@today-53.weeks).
|
||||||
|
select("todos.created_at, todos.completed_at").
|
||||||
|
reorder("todos.created_at DESC")
|
||||||
|
|
||||||
|
@max_weeks = difference_in_weeks(@today, @actions_started.last.created_at)
|
||||||
|
|
||||||
|
# cut off chart at 52 weeks = one year
|
||||||
|
@count = [52, @max_weeks].min
|
||||||
|
|
||||||
|
@actions_open_per_week_array = convert_to_weeks_running_from_today_array(@actions_started, @max_weeks+1)
|
||||||
|
@actions_open_per_week_array = cut_off_array(@actions_open_per_week_array, @count)
|
||||||
|
|
||||||
|
time_labels = Array.new(@count+1){ |i| "#{i}-#{i+1}" }
|
||||||
|
time_labels[0] = "< 1"
|
||||||
|
|
||||||
|
return {
|
||||||
|
datasets: [
|
||||||
|
{label: I18n.t('stats.open_per_week_legend.actions'), data: @actions_open_per_week_array},
|
||||||
|
],
|
||||||
|
labels: time_labels,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def day_of_week_all_data
|
||||||
|
@actions_creation_day = @user.todos.select("created_at")
|
||||||
|
@actions_completion_day = @user.todos.completed.select("completed_at")
|
||||||
|
|
||||||
|
# convert to array and fill in non-existing days
|
||||||
|
@actions_creation_day_array = Array.new(7) { |i| 0}
|
||||||
|
@actions_creation_day.each { |t| @actions_creation_day_array[ t.created_at.wday ] += 1 }
|
||||||
|
@max = @actions_creation_day_array.max
|
||||||
|
|
||||||
|
# convert to array and fill in non-existing days
|
||||||
|
@actions_completion_day_array = Array.new(7) { |i| 0}
|
||||||
|
@actions_completion_day.each { |t| @actions_completion_day_array[ t.completed_at.wday ] += 1 }
|
||||||
|
|
||||||
|
return {
|
||||||
|
datasets: [
|
||||||
|
{label: I18n.t('stats.labels.created'), data: @actions_creation_day_array},
|
||||||
|
{label: I18n.t('stats.labels.completed'), data: @actions_completion_day_array},
|
||||||
|
],
|
||||||
|
labels: I18n.t('date.day_names'),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def day_of_week_30days_data
|
||||||
|
@actions_creation_day = @user.todos.created_after(@cut_off_month).select("created_at")
|
||||||
|
@actions_completion_day = @user.todos.completed_after(@cut_off_month).select("completed_at")
|
||||||
|
|
||||||
|
# convert to hash to be able to fill in non-existing days
|
||||||
|
@max=0
|
||||||
|
@actions_creation_day_array = Array.new(7) { |i| 0}
|
||||||
|
@actions_creation_day.each { |r| @actions_creation_day_array[ r.created_at.wday ] += 1 }
|
||||||
|
|
||||||
|
# convert to hash to be able to fill in non-existing days
|
||||||
|
@actions_completion_day_array = Array.new(7) { |i| 0}
|
||||||
|
@actions_completion_day.each { |r| @actions_completion_day_array[r.completed_at.wday] += 1 }
|
||||||
|
|
||||||
|
return {
|
||||||
|
datasets: [
|
||||||
|
{label: I18n.t('stats.labels.created'), data: @actions_creation_day_array},
|
||||||
|
{label: I18n.t('stats.labels.completed'), data: @actions_completion_day_array},
|
||||||
|
],
|
||||||
|
labels: I18n.t('date.day_names'),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def time_of_day_all_data
|
||||||
|
@actions_creation_hour = @user.todos.select("created_at")
|
||||||
|
@actions_completion_hour = @user.todos.completed.select("completed_at")
|
||||||
|
|
||||||
|
# convert to hash to be able to fill in non-existing days
|
||||||
|
@actions_creation_hour_array = Array.new(24) { |i| 0}
|
||||||
|
@actions_creation_hour.each{|r| @actions_creation_hour_array[r.created_at.hour] += 1 }
|
||||||
|
|
||||||
|
# convert to hash to be able to fill in non-existing days
|
||||||
|
@actions_completion_hour_array = Array.new(24) { |i| 0}
|
||||||
|
@actions_completion_hour.each{|r| @actions_completion_hour_array[r.completed_at.hour] += 1 }
|
||||||
|
|
||||||
|
return {
|
||||||
|
datasets: [
|
||||||
|
{label: I18n.t('stats.labels.created'), data: @actions_creation_hour_array},
|
||||||
|
{label: I18n.t('stats.labels.completed'), data: @actions_completion_hour_array},
|
||||||
|
],
|
||||||
|
labels: @actions_creation_hour_array.each_with_index.map { |total, hour| [hour] },
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def time_of_day_30days_data
|
||||||
|
@actions_creation_hour = @user.todos.created_after(@cut_off_month).select("created_at")
|
||||||
|
@actions_completion_hour = @user.todos.completed_after(@cut_off_month).select("completed_at")
|
||||||
|
|
||||||
|
# convert to hash to be able to fill in non-existing days
|
||||||
|
@actions_creation_hour_array = Array.new(24) { |i| 0}
|
||||||
|
@actions_creation_hour.each{|r| @actions_creation_hour_array[r.created_at.hour] += 1 }
|
||||||
|
|
||||||
|
# convert to hash to be able to fill in non-existing days
|
||||||
|
@actions_completion_hour_array = Array.new(24) { |i| 0}
|
||||||
|
@actions_completion_hour.each{|r| @actions_completion_hour_array[r.completed_at.hour] += 1 }
|
||||||
|
|
||||||
|
return {
|
||||||
|
datasets: [
|
||||||
|
{label: I18n.t('stats.labels.created'), data: @actions_creation_hour_array},
|
||||||
|
{label: I18n.t('stats.labels.completed'), data: @actions_completion_hour_array},
|
||||||
|
],
|
||||||
|
labels: @actions_creation_hour_array.each_with_index.map { |total, hour| [hour] },
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
@ -73,5 +332,107 @@ module Stats
|
||||||
def completed
|
def completed
|
||||||
@completed ||= user.todos.completed.select("completed_at, created_at")
|
@completed ||= user.todos.completed.select("completed_at, created_at")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def interpolate_avg_for_current_month(set)
|
||||||
|
(set[0]*(1/percent_of_month) + set[1] + set[2]) / 3.0
|
||||||
|
end
|
||||||
|
|
||||||
|
def percent_of_month
|
||||||
|
Time.zone.now.day / Time.zone.now.end_of_month.day.to_f
|
||||||
|
end
|
||||||
|
|
||||||
|
# uses the supplied block to determine array of indexes in hash
|
||||||
|
# the block should return an array of indexes each is added to the hash and summed
|
||||||
|
def convert_to_array(records, upper_bound)
|
||||||
|
a = Array.new(upper_bound, 0)
|
||||||
|
records.each { |r| (yield r).each { |i| a[i] += 1 if a[i] } }
|
||||||
|
a
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_events_into_month_buckets(records, array_size, date_method_on_todo)
|
||||||
|
convert_to_array(records.select { |x| x.send(date_method_on_todo) }, array_size) { |r| [difference_in_months(@today, r.send(date_method_on_todo))]}
|
||||||
|
end
|
||||||
|
|
||||||
|
def convert_to_days_from_today_array(records, array_size, date_method_on_todo)
|
||||||
|
return convert_to_array(records, array_size){ |r| [difference_in_days(@today, r.send(date_method_on_todo))]}
|
||||||
|
end
|
||||||
|
|
||||||
|
def convert_to_weeks_from_today_array(records, array_size, date_method_on_todo)
|
||||||
|
return convert_to_array(records, array_size) { |r| [difference_in_weeks(@today, r.send(date_method_on_todo))]}
|
||||||
|
end
|
||||||
|
|
||||||
|
def convert_to_weeks_running_array(records, array_size)
|
||||||
|
return convert_to_array(records, array_size) { |r| [difference_in_weeks(r.completed_at, r.created_at)]}
|
||||||
|
end
|
||||||
|
|
||||||
|
def convert_to_weeks_running_from_today_array(records, array_size)
|
||||||
|
return convert_to_array(records, array_size) { |r| week_indexes_of(r) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def week_indexes_of(record)
|
||||||
|
a = []
|
||||||
|
start_week = difference_in_weeks(@today, record.created_at)
|
||||||
|
end_week = record.completed_at ? difference_in_weeks(@today, record.completed_at) : 0
|
||||||
|
end_week.upto(start_week) { |i| a << i };
|
||||||
|
return a
|
||||||
|
end
|
||||||
|
|
||||||
|
def cut_off_array_with_sum(array, cut_off)
|
||||||
|
# +1 to hold sum of rest
|
||||||
|
a = Array.new(cut_off+1){|i| array[i]||0}
|
||||||
|
# add rest of array to last elem
|
||||||
|
a[cut_off] += array.inject(:+) - a.inject(:+)
|
||||||
|
return a
|
||||||
|
end
|
||||||
|
|
||||||
|
def cut_off_array(array, cut_off)
|
||||||
|
return Array.new(cut_off){|i| array[i]||0}
|
||||||
|
end
|
||||||
|
|
||||||
|
def convert_to_cumulative_array(array, max)
|
||||||
|
# calculate fractions
|
||||||
|
a = Array.new(array.size){|i| array[i]*100.0/max}
|
||||||
|
# make cumulative
|
||||||
|
1.upto(array.size-1){ |i| a[i] += a[i-1] }
|
||||||
|
return a
|
||||||
|
end
|
||||||
|
|
||||||
|
def difference_in_months(date1, date2)
|
||||||
|
return (date1.utc.year - date2.utc.year)*12 + (date1.utc.month - date2.utc.month)
|
||||||
|
end
|
||||||
|
|
||||||
|
# assumes date1 > date2
|
||||||
|
def difference_in_days(date1, date2)
|
||||||
|
return ((date1.utc.at_midnight-date2.utc.at_midnight)/SECONDS_PER_DAY).to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
# assumes date1 > date2
|
||||||
|
def difference_in_weeks(date1, date2)
|
||||||
|
return difference_in_days(date1, date2) / 7
|
||||||
|
end
|
||||||
|
|
||||||
|
def three_month_avg(set, i)
|
||||||
|
(set.fetch(i){ 0 } + set.fetch(i+1){ 0 } + set.fetch(i+2){ 0 }) / 3.0
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_three_month_avg(set,upper_bound)
|
||||||
|
(0..upper_bound-1).map { |i| three_month_avg(set, i) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def compute_running_avg_array(set, upper_bound)
|
||||||
|
result = set_three_month_avg(set, upper_bound)
|
||||||
|
result[upper_bound-1] = result[upper_bound-1] * 3 if upper_bound == set.length
|
||||||
|
result[upper_bound-2] = result[upper_bound-2] * 3 / 2 if upper_bound > 1 and upper_bound == set.length
|
||||||
|
result[0] = "null"
|
||||||
|
result
|
||||||
|
end # unsolved, not triggered, edge case for set.length == upper_bound + 1
|
||||||
|
|
||||||
|
def month_label(i)
|
||||||
|
I18n.t('date.month_names')[ (Time.zone.now.mon - i -1 ) % 12 + 1 ]
|
||||||
|
end
|
||||||
|
|
||||||
|
def array_of_month_labels(count)
|
||||||
|
Array.new(count) { |i| month_label(i) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -13,15 +13,5 @@ module Stats
|
||||||
def running_actions
|
def running_actions
|
||||||
@running_actions ||= Stats::TopContextsQuery.new(user, :limit => 5, :running => true).result
|
@running_actions ||= Stats::TopContextsQuery.new(user, :limit => 5, :running => true).result
|
||||||
end
|
end
|
||||||
|
|
||||||
def charts
|
|
||||||
@charts = %w{
|
|
||||||
context_total_actions_data
|
|
||||||
context_running_actions_data
|
|
||||||
}.map do |action|
|
|
||||||
Stats::Chart.new(action, :height => 325)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
module Stats
|
|
||||||
class PieChartData
|
|
||||||
|
|
||||||
attr_reader :all_totals, :alpha, :title
|
|
||||||
def initialize(all_totals, title, alpha)
|
|
||||||
@all_totals = all_totals
|
|
||||||
@title = title
|
|
||||||
@alpha = alpha
|
|
||||||
end
|
|
||||||
|
|
||||||
def values
|
|
||||||
@values ||= Array.new(slices) do |i|
|
|
||||||
chart_totals[i]['total'] * 100 / sum
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def labels
|
|
||||||
@labels ||= Array.new(slices) do |i|
|
|
||||||
chart_totals[i]['name'].truncate(15, :omission => '...')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def ids
|
|
||||||
@ids ||= Array.new(slices) do |i|
|
|
||||||
chart_totals[i]['id']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def sum
|
|
||||||
@sum ||= totals.inject(0) do |sum, total|
|
|
||||||
sum + total
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def pie_cutoff
|
|
||||||
10
|
|
||||||
end
|
|
||||||
|
|
||||||
def slices
|
|
||||||
@slices ||= [all_totals.size, pie_cutoff].min
|
|
||||||
end
|
|
||||||
|
|
||||||
def subtotal(from, to)
|
|
||||||
totals[from..to].inject(0) do |sum, total|
|
|
||||||
sum + total
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def chart_totals
|
|
||||||
unless @chart_totals
|
|
||||||
@chart_totals = first_n_totals(10)
|
|
||||||
if all_totals.size > pie_cutoff
|
|
||||||
@chart_totals[-1] = other
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@chart_totals
|
|
||||||
end
|
|
||||||
|
|
||||||
def first_n_totals(n)
|
|
||||||
# create a duplicate so that we don't accidentally
|
|
||||||
# overwrite the original array
|
|
||||||
Array.new(slices) do |i|
|
|
||||||
{
|
|
||||||
'name' => all_totals[i]['name'],
|
|
||||||
'total' => all_totals[i]['total'],
|
|
||||||
'id' => all_totals[i]['id']
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def other
|
|
||||||
{
|
|
||||||
'name' => I18n.t('stats.other_actions_label'),
|
|
||||||
'id' => -1,
|
|
||||||
'total' => subtotal(slices-1, all_totals.size-1)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def totals
|
|
||||||
@totals ||= all_totals.map { |item| item['total'] }
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,17 +1,111 @@
|
||||||
<%= render :partial => 'time_to_complete', :locals => {:ttc => actions.ttc} -%>
|
<%= render :partial => 'time_to_complete', :locals => {:ttc => actions.ttc} -%>
|
||||||
|
<%
|
||||||
|
options = {
|
||||||
|
width: "400px",
|
||||||
|
height: "400px",
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
responsive: false,
|
||||||
|
plugins: {
|
||||||
|
colorschemes: {
|
||||||
|
scheme: 'brewer.Paired12',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
%>
|
||||||
<p><%= t('stats.actions_actions_avg_created_30days', :count => (actions.created_last30days*10.0/30.0).round/10.0 )%>
|
<p><%= t('stats.actions_actions_avg_created_30days', :count => (actions.created_last30days*10.0/30.0).round/10.0 )%>
|
||||||
<%= t('stats.actions_avg_completed_30days', :count => (actions.done_last30days*10.0/30.0).round/10.0 )%>
|
<%= t('stats.actions_avg_completed_30days', :count => (actions.done_last30days*10.0/30.0).round/10.0 )%>
|
||||||
<%= t('stats.actions_avg_created', :count => (actions.created_last12months*10.0/12.0).round/10.0 )%>
|
<%= t('stats.actions_avg_created', :count => (actions.created_last12months*10.0/12.0).round/10.0 )%>
|
||||||
<%= t('stats.actions_avg_completed', :count => (actions.done_last12months*10.0/12.0).round/10.0 )%></p>
|
<%= t('stats.actions_avg_completed', :count => (actions.done_last12months*10.0/12.0).round/10.0 )%></p>
|
||||||
|
|
||||||
<% actions.completion_charts.each do |chart| %><%=
|
<%= bar_chart actions.done_last30days_data, options.merge({
|
||||||
render :partial => 'chart', :locals => {:chart => chart}
|
scales: {
|
||||||
-%><% end %>
|
xAxes: [{ scaleLabel: { display: true, labelString: t('stats.legend.number_of_days')}}],
|
||||||
|
yAxes: [{ scaleLabel: { display: true, labelString: t('stats.legend.number_of_actions')}}],
|
||||||
|
},
|
||||||
|
'title': {'display': true, 'text': t('stats.actions_30days_title')},
|
||||||
|
}) %>
|
||||||
|
|
||||||
|
<%= bar_chart actions.done_last12months_data, options.merge({
|
||||||
|
scales: {
|
||||||
|
xAxes: [{ scaleLabel: { display: true, labelString: t('stats.legend.months_ago')}}],
|
||||||
|
yAxes: [{ scaleLabel: { display: true, labelString: t('stats.legend.number_of_actions')}}],
|
||||||
|
},
|
||||||
|
'title': {'display': true, 'text': t('stats.actions_lastyear_title')},
|
||||||
|
'onClick': 'function() { window.location.href = "' + url_for(:controller => 'stats', :action => 'actions_done_last_years') + '"; }',
|
||||||
|
}) %>
|
||||||
|
|
||||||
|
<%
|
||||||
|
# TODO: There should be separate scales for percentage and amount of tasks so that the max of both is in the top of the chart, ie. the left y-axis should be "Percentage".
|
||||||
|
%>
|
||||||
|
<%= bar_chart actions.completion_time_data, options.merge({
|
||||||
|
scales: {
|
||||||
|
xAxes: [{ scaleLabel: { display: true, labelString: t('stats.legend.running_time')}}],
|
||||||
|
yAxes: [{ scaleLabel: { display: true, labelString: t('stats.legend.actions')}}],
|
||||||
|
},
|
||||||
|
'title': {'display': true, 'text': t('stats.action_completion_time_title')}}) %>
|
||||||
|
|
||||||
<br style="clear:both">
|
<br style="clear:both">
|
||||||
|
|
||||||
<% actions.timing_charts.each do |chart| %><%=
|
<%
|
||||||
render :partial => 'chart', :locals => {:chart => chart}
|
# TODO: There should be separate scales for percentage and amount of tasks so that the max of both is in the top of the chart, ie. the left y-axis should be "Percentage".
|
||||||
-%><% end %>
|
%>
|
||||||
|
<%= bar_chart actions.visible_running_time_data, options.merge({
|
||||||
|
scales: {
|
||||||
|
xAxes: [{ scaleLabel: { display: true, labelString: t('stats.running_time_legend.weeks')}}],
|
||||||
|
yAxes: [{ scaleLabel: { display: true, labelString: t('stats.running_time_legend.actions')}}],
|
||||||
|
},
|
||||||
|
'title': {'display': true, 'text': t('stats.current_running_time_of_incomplete_visible_actions')},
|
||||||
|
'onClick': 'function(event, array) { window.location.href = "' + url_for(:controller => 'stats', :action => 'show_selected_actions_from_chart', :id => "avrt") + '?index=" + array[0]._index; }',
|
||||||
|
}) %>
|
||||||
|
|
||||||
|
<%
|
||||||
|
# TODO: There should be separate scales for percentage and amount of tasks so that the max of both is in the top of the chart, ie. the left y-axis should be "Percentage".
|
||||||
|
%>
|
||||||
|
<%= bar_chart actions.running_time_data, options.merge({
|
||||||
|
scales: {
|
||||||
|
xAxes: [{ scaleLabel: { display: true, labelString: t('stats.running_time_all_legend.running_time')}}],
|
||||||
|
yAxes: [{ scaleLabel: { display: true, labelString: t('stats.running_time_all_legend.actions')}}],
|
||||||
|
},
|
||||||
|
'title': {'display': true, 'text': t('stats.running_time_all')},
|
||||||
|
'onClick': 'function(event, array) { window.location.href = "' + url_for(:controller => 'stats', :action => 'show_selected_actions_from_chart', :id => "art") + '?index=" + array[0]._index; }',
|
||||||
|
}) %>
|
||||||
|
|
||||||
|
<br style="clear:both">
|
||||||
|
|
||||||
|
<%= bar_chart actions.open_per_week_data, options.merge({
|
||||||
|
scales: {
|
||||||
|
xAxes: [{ scaleLabel: { display: true, labelString: t('stats.open_per_week_legend.weeks')}}],
|
||||||
|
yAxes: [{ scaleLabel: { display: true, labelString: t('stats.open_per_week_legend.actions')}}],
|
||||||
|
},
|
||||||
|
'title': {'display': true, 'text': t('stats.open_per_week')}}) %>
|
||||||
|
|
||||||
|
<%= bar_chart actions.day_of_week_all_data, options.merge({
|
||||||
|
scales: {
|
||||||
|
xAxes: [{ scaleLabel: { display: true, labelString: t('stats.actions_day_of_week_legend.day_of_week')}}],
|
||||||
|
yAxes: [{ scaleLabel: { display: true, labelString: t('stats.actions_day_of_week_legend.number_of_actions')}}],
|
||||||
|
},
|
||||||
|
'title': {'display': true, 'text': t('stats.actions_day_of_week_title')}}) %>
|
||||||
|
|
||||||
|
<%= bar_chart actions.day_of_week_30days_data, options.merge({
|
||||||
|
scales: {
|
||||||
|
xAxes: [{ scaleLabel: { display: true, labelString: t('stats.actions_dow_30days_legend.day_of_week')}}],
|
||||||
|
yAxes: [{ scaleLabel: { display: true, labelString: t('stats.actions_dow_30days_legend.number_of_actions')}}],
|
||||||
|
},
|
||||||
|
'title': {'display': true, 'text': t('stats.actions_dow_30days_title')}}) %>
|
||||||
|
|
||||||
|
<br style="clear:both">
|
||||||
|
|
||||||
|
<%= bar_chart actions.time_of_day_all_data, options.merge({
|
||||||
|
scales: {
|
||||||
|
xAxes: [{ scaleLabel: { display: true, labelString: t('stats.time_of_day_legend.time_of_day')}}],
|
||||||
|
yAxes: [{ scaleLabel: { display: true, labelString: t('stats.time_of_day_legend.number_of_actions')}}],
|
||||||
|
},
|
||||||
|
'title': {'display': true, 'text': t('stats.time_of_day')}}) %>
|
||||||
|
|
||||||
|
<%= bar_chart actions.time_of_day_30days_data, options.merge({
|
||||||
|
scales: {
|
||||||
|
xAxes: [{ scaleLabel: { display: true, labelString: t('stats.tod30_legend.time_of_day')}}],
|
||||||
|
yAxes: [{ scaleLabel: { display: true, labelString: t('stats.tod30_legend.number_of_actions')}}],
|
||||||
|
},
|
||||||
|
'title': {'display': true, 'text': t('stats.tod30')}}) %>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
<% @swf_count ||= 0 -%>
|
|
||||||
<div class="open-flash-chart"></div>
|
|
||||||
|
|
@ -1,6 +1,51 @@
|
||||||
<% contexts.charts.each do |chart| %><%=
|
<br style="clear:both">
|
||||||
render :partial => 'chart', :locals => {:chart => chart}
|
|
||||||
-%><% end %>
|
<% data = {
|
||||||
|
datasets: [{
|
||||||
|
data: Array.new,
|
||||||
|
}],
|
||||||
|
labels: Array.new,
|
||||||
|
ids: Array.new,
|
||||||
|
}
|
||||||
|
Stats::TopContextsQuery.new(current_user).result.map { |context|
|
||||||
|
data[:datasets][0][:data].append(context.total)
|
||||||
|
data[:labels].append(context.name)
|
||||||
|
data[:ids].append(context.id)
|
||||||
|
}
|
||||||
|
options = {
|
||||||
|
width: "400px",
|
||||||
|
height: "400px",
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
responsive: false,
|
||||||
|
plugins: {
|
||||||
|
colorschemes: {
|
||||||
|
scheme: 'brewer.Paired12',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
%>
|
||||||
|
<% #TODO: Move data handling to model. Show value as percentage %>
|
||||||
|
<%= pie_chart data, options.merge({
|
||||||
|
'title': {'display': true, 'text': t('stats.spread_of_actions_for_all_context')},
|
||||||
|
'onClick': 'function(event, array) { window.location.href = "' + url_for(:controller => 'contexts', :action => 'show', :id => -1).gsub('-1', '') + '" + array[0]._chart.chart.data.ids[array[0]._index]; }'
|
||||||
|
}) %>
|
||||||
|
|
||||||
|
<% data = {
|
||||||
|
datasets: [{
|
||||||
|
data: Array.new,
|
||||||
|
}],
|
||||||
|
labels: Array.new,
|
||||||
|
ids: Array.new,
|
||||||
|
}
|
||||||
|
Stats::TopContextsQuery.new(current_user, :running => true).result.map { |context|
|
||||||
|
data[:datasets][0][:data].append(context.total)
|
||||||
|
data[:labels].append(context.name)
|
||||||
|
data[:ids].append(context.id)
|
||||||
|
}
|
||||||
|
%>
|
||||||
|
<%= pie_chart data, options.merge({
|
||||||
|
'onClick': 'function(event, array) { window.location.href = "' + url_for(:controller => 'contexts', :action => 'show', :id => -1).gsub('-1', '') + '" + array[0]._chart.chart.data.ids[array[0]._index]; }',
|
||||||
|
'title': {'display': true, 'text': t('stats.spread_of_running_actions_for_visible_contexts')}}) %>
|
||||||
|
|
||||||
<br style="clear:both">
|
<br style="clear:both">
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
<%-
|
|
||||||
time_labels = Array.new(@count){ |i| "#{i}-#{i+1}" }
|
|
||||||
time_labels[0] = t('stats.within_one')
|
|
||||||
time_labels[@count] = "> #{@count}"
|
|
||||||
-%>
|
|
||||||
&title=<%= t('stats.action_completion_time_title') %>,{font-size:16},&
|
|
||||||
&y_legend=<%= t('stats.legend.actions') %>,10,0x8010A0&
|
|
||||||
&y2_legend=<%= t('stats.legend.percentage') %>,10,0xFF0000&
|
|
||||||
&x_legend=<%= t('stats.legend.running_time') %>,12,0x736AFF&
|
|
||||||
&y_ticks=5,10,5&
|
|
||||||
&filled_bar=50,0x9933CC,0x8010A0&
|
|
||||||
&values=<%= @actions_completion_time_array.join(",")%>&
|
|
||||||
&line_2=2,0xFF0000&
|
|
||||||
&values_2=<%= @cum_percent_done.join(",")%>&
|
|
||||||
&x_labels=<%= time_labels.join(",")%>&
|
|
||||||
&y_min=0&
|
|
||||||
<%
|
|
||||||
# add one to @max for people who have no actions completed yet.
|
|
||||||
# OpenFlashChart cannot handle y_max=0
|
|
||||||
-%>
|
|
||||||
&y_max=<%=1+@max_actions+@max_actions/10-%>&
|
|
||||||
&show_y2=true&
|
|
||||||
&y2_lines=2&
|
|
||||||
&y2_min=0&
|
|
||||||
&y2_max=100&
|
|
||||||
&x_label_style=9,,2,1&
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
&title=<%= t('stats.actions_dow_30days_title') %>,{font-size:16},&
|
|
||||||
&y_legend=<%= t('stats.actions_dow_30days_legend.number_of_actions') %>,10,0x736AFF&
|
|
||||||
&x_legend=<%= t('stats.actions_dow_30days_legend.day_of_week') %>,10,0x736AFF&
|
|
||||||
&y_ticks=5,10,5&
|
|
||||||
&filled_bar=50,0x9933CC,0x8010A0,<%= t('stats.labels.created') %>,8&
|
|
||||||
&filled_bar_2=50,0x0066CC,0x0066CC,<%= t('stats.labels.completed') %>,8&
|
|
||||||
&values=<%= @actions_creation_day_array.join(",") %>&
|
|
||||||
&values_2=<%= @actions_completion_day_array.join(",") %>&
|
|
||||||
&x_labels=<%= t('date.day_names').join(",") %>&
|
|
||||||
&y_min=0&
|
|
||||||
<%
|
|
||||||
# add one to @max for people who have no actions completed yet.
|
|
||||||
# OpenFlashChart cannot handle y_max=0
|
|
||||||
-%>
|
|
||||||
&y_max=<%=@max+1 -%>&
|
|
||||||
&x_label_style=9,,2,1&
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
&title=<%= t('stats.actions_day_of_week_title') %>,{font-size:16},&
|
|
||||||
&y_legend=<%= t('stats.actions_day_of_week_legend.number_of_actions') %>,10,0x736AFF&
|
|
||||||
&x_legend=<%= t('stats.actions_day_of_week_legend.day_of_week') %>,10,0x736AFF&
|
|
||||||
&y_ticks=5,10,5&
|
|
||||||
&filled_bar=50,0x9933CC,0x8010A0,<%= t('stats.labels.created') %>,8&
|
|
||||||
&filled_bar_2=50,0x0066CC,0x0066CC,<%= t('stats.labels.completed') %>,8&
|
|
||||||
&values=<%= @actions_creation_day_array.join(",") %>&
|
|
||||||
&values_2=<%= @actions_completion_day_array.join(",") %>&
|
|
||||||
&x_labels=<%= t('date.day_names').join(",") %>&
|
|
||||||
&y_min=0&
|
|
||||||
<%
|
|
||||||
# add one to @max for people who have no actions completed yet.
|
|
||||||
# OpenFlashChart cannot handle y_max=0
|
|
||||||
-%>
|
|
||||||
&y_max=<%=@max+1 -%>&
|
|
||||||
&x_label_style=9,,2,1&
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
<%-
|
|
||||||
url = url_for :controller => 'stats', :action => 'actions_done_last_years'
|
|
||||||
-%>
|
|
||||||
&title=<%= t('stats.actions_lastyear_title') %>,{font-size:16},&
|
|
||||||
&y_legend=<%= t('stats.legend.number_of_actions') %>,12,0x736AFF&
|
|
||||||
&x_legend=<%= t('stats.legend.months_ago') %>,12,0x736AFF&
|
|
||||||
&y_ticks=5,10,5&
|
|
||||||
&filled_bar=50,0x9933CC,0x8010A0,<%= t('stats.labels.created')%>,9&
|
|
||||||
&filled_bar_2=50,0x0066CC,0x0066CC,<%= t('stats.labels.completed') %>,9&
|
|
||||||
&line_3=2,0x00FF00, <%= t('stats.labels.avg_created') %>, 9&
|
|
||||||
&line_4=2,0xFF0000, <%= t('stats.labels.avg_completed') %>, 9&
|
|
||||||
&line_5=2,0x007700, <%= t('stats.labels.month_avg_created', :months => 3) %>, 9&
|
|
||||||
&line_6=2,0xAA0000, <%= t('stats.labels.month_avg_completed', :months => 3) %>, 9&
|
|
||||||
&line_7=1,0xAA0000&
|
|
||||||
&line_8=1,0x007700&
|
|
||||||
&values=<%= @actions_created_last12months_array.join(",")%>&
|
|
||||||
&links=<%= Array.new(13,url).join(",") %>&
|
|
||||||
&links_2=<%= Array.new(13,url).join(",") %>&
|
|
||||||
&values_2=<%= @actions_done_last12months_array.join(",")%>&
|
|
||||||
&values_3=<%= @created_count_array.join(",")%>&
|
|
||||||
&values_4=<%= @done_count_array.join(",")%>&
|
|
||||||
&values_5=<%= @actions_created_avg_last12months_array.join(",")%>&
|
|
||||||
&values_6=<%= @actions_done_avg_last12months_array.join(",")%>&
|
|
||||||
&values_7=<%= @interpolated_actions_created_this_month%>,<%=@actions_done_avg_last12months_array[1]%>&
|
|
||||||
&values_8=<%= @interpolated_actions_done_this_month%>,<%=@actions_created_avg_last12months_array[1]%>&
|
|
||||||
&x_labels=<%= array_of_month_labels(@done_count_array.size).join(",")%>&
|
|
||||||
&y_min=0&
|
|
||||||
<% # add one to @max for people who have no actions completed yet.
|
|
||||||
# OpenFlashChart cannot handle y_max=0 -%>
|
|
||||||
&y_max=<%=@max+@max/10+1-%>&
|
|
||||||
&x_label_style=9,,2,&
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
<%-
|
|
||||||
created_count_array = Array.new(30){ |i| @actions_created_last30days.size/30.0 }
|
|
||||||
done_count_array = Array.new(30){ |i| @actions_done_last30days.size/30.0 }
|
|
||||||
# TODO: make the strftime i18n proof
|
|
||||||
time_labels = Array.new(30){ |i| l(Time.zone.now-i.days, :format => :stats) }
|
|
||||||
-%>
|
|
||||||
&title=<%= t('stats.actions_30days_title') %>,{font-size:16},&
|
|
||||||
&y_legend=<%= t('stats.legend.number_of_actions') %>,12,0x736AFF&
|
|
||||||
&x_legend=<%= t('stats.legend.number_of_days') %>,12,0x736AFF&
|
|
||||||
&y_ticks=5,10,5&
|
|
||||||
&filled_bar=50,0x9933CC,0x8010A0,<%= t('stats.labels.created') %>,9&
|
|
||||||
&filled_bar_2=50,0x0066CC,0x0066CC,<%= t('stats.labels.completed') %>,9&
|
|
||||||
&line_3=3,0x00FF00, <%= t('stats.labels.avg_created') %>, 9&
|
|
||||||
&line_4=3,0xFF0000, <%= t('stats.labels.avg_completed') %>, 9&
|
|
||||||
&values=<%= @actions_created_last30days_array.join(",")%>&
|
|
||||||
&values_2=<%= @actions_done_last30days_array.join(",")%>&
|
|
||||||
&values_3=<%= created_count_array.join(",")%>&
|
|
||||||
&values_4=<%= done_count_array.join(",")%>&
|
|
||||||
&x_labels=<%= time_labels.join(",")%>&
|
|
||||||
&y_min=0&
|
|
||||||
<% # max + 10% for some extra space at the top
|
|
||||||
# add one to @max for people who have no actions completed yet.
|
|
||||||
# OpenFlashChart cannot handle y_max=0
|
|
||||||
-%>
|
|
||||||
&y_max=<%=@max+@max/10+1 -%>&
|
|
||||||
&x_label_style=9,,2,3&
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
<%-
|
|
||||||
time_labels = Array.new(@count+1){ |i| "#{i}-#{i+1}" }
|
|
||||||
time_labels[0] = "< 1"
|
|
||||||
-%>
|
|
||||||
&title=<%= t('stats.open_per_week') %>,{font-size:16},&
|
|
||||||
&y_legend=<%= t('stats.open_per_week_legend.actions') %>,10,0x736AFF&
|
|
||||||
&x_legend=<%= t('stats.open_per_week_legend.weeks') %>,11,0x736AFF&
|
|
||||||
&y_ticks=5,10,5&
|
|
||||||
&filled_bar=50,0x9933CC,0x8010A0&
|
|
||||||
&values=<%= @actions_open_per_week_array.join(",") -%>&
|
|
||||||
&x_labels=<%= time_labels.join(",")%>&
|
|
||||||
&y_min=0&
|
|
||||||
<%
|
|
||||||
# add one to @max for people who have no actions completed yet.
|
|
||||||
# OpenFlashChart cannot handle y_max=0
|
|
||||||
-%>
|
|
||||||
&y_max=<%=1+@max_actions+@max_actions/10-%>&
|
|
||||||
&x_label_style=9,,2,2&
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
<%-
|
|
||||||
url_labels = Array.new(@count){ |i| url_for(:controller => 'stats', :action => 'show_selected_actions_from_chart', :index => i, :id=> "art") }
|
|
||||||
url_labels[@count]=url_for(:controller => 'stats', :action => 'show_selected_actions_from_chart', :index => @count, :id=> "art_end")
|
|
||||||
|
|
||||||
time_labels = Array.new(@count){ |i| "#{i}-#{i+1}" }
|
|
||||||
time_labels[0] = "< 1"
|
|
||||||
time_labels[@count] = "> #{@count}"
|
|
||||||
-%>
|
|
||||||
&title=<%= t('stats.running_time_all') %>,{font-size:16},&
|
|
||||||
&y_legend=<%= t('stats.running_time_all_legend.actions') %>,10,0x736AFF&
|
|
||||||
&y2_legend=<%= t('stats.running_time_all_legend.percentage') %>,10,0xFF0000&
|
|
||||||
&x_legend=<%= t('stats.running_time_all_legend.running_time') %>,11,0x736AFF&
|
|
||||||
&y_ticks=5,10,5&
|
|
||||||
&filled_bar=50,0x9933CC,0x8010A0&
|
|
||||||
&values=<%= @actions_running_time_array.join(",") -%>&
|
|
||||||
&links=<%= url_labels.join(",") %>&
|
|
||||||
&line_2=2,0xFF0000&
|
|
||||||
&values_2=<%= @cum_percent_done.join(",") %>&
|
|
||||||
&x_labels=<%= time_labels.join(",") %> &
|
|
||||||
&y_min=0&
|
|
||||||
<%
|
|
||||||
# add one to @max for people who have no actions completed yet.
|
|
||||||
# OpenFlashChart cannot handle y_max=0
|
|
||||||
-%>
|
|
||||||
&y_max=<%=1+@max_actions+@max_actions/10-%>&
|
|
||||||
&x_label_style=9,,2,2&
|
|
||||||
&show_y2=true&
|
|
||||||
&y2_lines=2&
|
|
||||||
&y2_min=0&
|
|
||||||
&y2_max=100&
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
&title=<%= t('stats.tod30') %>,{font-size:16},&
|
|
||||||
&y_legend=<%= t('stats.tod30_legend.number_of_actions') %>,12,0x736AFF&
|
|
||||||
&x_legend=<%= t('stats.tod30_legend.time_of_day') %>,12,0x736AFF&
|
|
||||||
&y_ticks=5,10,5&
|
|
||||||
&filled_bar=50,0x9933CC,0x8010A0,<%= t('stats.labels.created') %>,8&
|
|
||||||
&filled_bar_2=50,0x0066CC,0x0066CC,<%= t('stats.labels.completed') %>,8&
|
|
||||||
&values=<%
|
|
||||||
0.upto 22 do |i| -%>
|
|
||||||
<%=@actions_creation_hour_array[i] -%>,
|
|
||||||
<% end -%><%=@actions_creation_hour_array[23]%>&
|
|
||||||
&values_2=<%
|
|
||||||
0.upto 22 do |i| -%>
|
|
||||||
<%=@actions_completion_hour_array[i] -%>,
|
|
||||||
<% end -%><%=@actions_completion_hour_array[23]%>&
|
|
||||||
&x_labels= <%
|
|
||||||
0.upto 22 do |i| -%>
|
|
||||||
<%=i-%>,
|
|
||||||
<% end -%>23&
|
|
||||||
&y_min=0&
|
|
||||||
<% # add one to @max for people who have no actions completed yet.
|
|
||||||
# OpenFlashChart cannot handle y_max=0 -%>
|
|
||||||
&y_max=<%=@max+1 -%>&
|
|
||||||
&x_label_style=9,,1,1&
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
&title=<%= t('stats.time_of_day') %>,{font-size:16},&
|
|
||||||
&y_legend=<%= t('stats.time_of_day_legend.number_of_actions') %>,12,0x736AFF&
|
|
||||||
&x_legend=<%= t('stats.time_of_day_legend.time_of_day') %>,12,0x736AFF&
|
|
||||||
&y_ticks=5,10,5&
|
|
||||||
&filled_bar=50,0x9933CC,0x8010A0,<%= t('stats.labels.created') %>,8&
|
|
||||||
&filled_bar_2=50,0x0066CC,0x0066CC,<%= t('stats.labels.completed') %>,8&
|
|
||||||
&values=<%
|
|
||||||
0.upto 22 do |i| -%>
|
|
||||||
<%=@actions_creation_hour_array[i] -%>,
|
|
||||||
<% end -%><%=@actions_creation_hour_array[23]%>&
|
|
||||||
&values_2=<%
|
|
||||||
0.upto 22 do |i| -%>
|
|
||||||
<%=@actions_completion_hour_array[i] -%>,
|
|
||||||
<% end -%><%=@actions_completion_hour_array[23]%>&
|
|
||||||
&x_labels= <%
|
|
||||||
0.upto 22 do |i| -%>
|
|
||||||
<%=i-%>,
|
|
||||||
<% end -%>23&
|
|
||||||
&y_min=0&
|
|
||||||
<% # add one to @max for people who have no actions completed yet.
|
|
||||||
# OpenFlashChart cannot handle y_max=0 -%>
|
|
||||||
&y_max=<%=@max+1 -%>&
|
|
||||||
&x_label_style=9,,1,1&
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
<%-
|
|
||||||
url_labels = Array.new(@count){ |i| url_for(:controller => 'stats', :action => 'show_selected_actions_from_chart', :index => i, :id=> "avrt") }
|
|
||||||
url_labels[@count]=url_for(:controller => 'stats', :action => 'show_selected_actions_from_chart', :index => @count, :id=> "avrt_end")
|
|
||||||
|
|
||||||
time_labels = Array.new(@count){ |i| "#{i}-#{i+1}" }
|
|
||||||
time_labels[0] = "< 1"
|
|
||||||
time_labels[@count] = "> #{@count}"
|
|
||||||
-%>
|
|
||||||
&title=<%= t('stats.current_running_time_of_incomplete_visible_actions') %>,{font-size:16},&
|
|
||||||
&y_legend=<%= t('stats.running_time_legend.actions') %>,10,0x736AFF&
|
|
||||||
&y2_legend=<%= t('stats.running_time_legend.percentage') %>,10,0xFF0000&
|
|
||||||
&x_legend=<%= t('stats.running_time_legend.weeks') %>,11,0x736AFF&
|
|
||||||
&y_ticks=5,10,5&
|
|
||||||
&filled_bar=50,0x9933CC,0x8010A0&
|
|
||||||
&values=<%= @actions_running_time_array.join(",") -%>&
|
|
||||||
&links=<%= url_labels.join(",") %>&
|
|
||||||
&line_2=2,0xFF0000&
|
|
||||||
&values_2=<%= @cum_percent_done.join(",") -%>&
|
|
||||||
&x_labels=<%= time_labels.join(",")%>&
|
|
||||||
&y_min=0&
|
|
||||||
<%
|
|
||||||
# add one to @max for people who have no actions completed yet.
|
|
||||||
# OpenFlashChart cannot handle y_max=0
|
|
||||||
-%>
|
|
||||||
&y_max=<%=1+@max_actions+@max_actions/10-%>&
|
|
||||||
&x_label_style=9,,2,2&
|
|
||||||
&show_y2=true&
|
|
||||||
&y2_lines=2&
|
|
||||||
&y2_min=0&
|
|
||||||
&y2_max=100&
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
<%= javascript_include_tag "swf_fu" %>
|
|
||||||
|
|
||||||
<div class="stats_content">
|
<div class="stats_content">
|
||||||
<h2><%= t('stats.totals') %></h2>
|
<h2><%= t('stats.totals') %></h2>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
&title=<%= @data.title %>,{font-size:16}&
|
|
||||||
&pie=<%= @data.alpha %>,#505050,{font-size: 12px; color: #404040;}&
|
|
||||||
&x_axis_steps=1& &y_ticks=5,10,5& &line=3,#87421F& &y_min=0& &y_max=20&
|
|
||||||
&values=<%= @data.values.join(",") %>&
|
|
||||||
&pie_labels=<%= @data.labels.join(",") %>&
|
|
||||||
&links=<%= @data.ids.map{|id| context_path(id)}.join(",") %>&
|
|
||||||
&colours=#d01f3c,#356aa0,#C79810,#c61fd0,#1fc6d0,#1fd076,#72d01f,#c6d01f,#d0941f,#40941f&
|
|
||||||
&tool_tip=#x_label#: #val#%25&
|
|
||||||
&x_label_style=9,,2,1&
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
<%= render :partial => 'chart', :locals => {:chart => @chart} -%>
|
|
||||||
<br/>
|
|
||||||
<p>
|
<p>
|
||||||
<%= t('stats.click_to_update_actions') %> <%= raw t('stats.click_to_return', :link => link_to(t('stats.click_to_return_link'), stats_path)) %>
|
<%= raw t('stats.click_to_return', :link => link_to(t('stats.click_to_return_link'), stats_path)) %>
|
||||||
<%
|
<%
|
||||||
unless @further
|
unless @further
|
||||||
-%>
|
-%>
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class ContextActionsDataTest < ActionController::TestCase
|
|
||||||
tests StatsController
|
|
||||||
|
|
||||||
def test_total_with_more_than_10_items
|
|
||||||
login_as(:admin_user)
|
|
||||||
contexts = [
|
|
||||||
{'id' => 1, 'name' => 'one', 'total' => 11},
|
|
||||||
{'id' => 2, 'name' => 'two', 'total' => 4},
|
|
||||||
{'id' => 3, 'name' => 'three', 'total' => 8}
|
|
||||||
]
|
|
||||||
Stats::TopContextsQuery.any_instance.stubs(:result).returns contexts
|
|
||||||
|
|
||||||
get :context_total_actions_data
|
|
||||||
|
|
||||||
assert_equal [47, 17, 34], assigns[:data].values
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_running_actions
|
|
||||||
login_as(:admin_user)
|
|
||||||
contexts = [
|
|
||||||
{'id' => 1, 'name' => 'one', 'total' => 11},
|
|
||||||
{'id' => 2, 'name' => 'two', 'total' => 4},
|
|
||||||
{'id' => 3, 'name' => 'three', 'total' => 8}
|
|
||||||
]
|
|
||||||
Stats::TopContextsQuery.any_instance.stubs(:result).returns contexts
|
|
||||||
|
|
||||||
get :context_running_actions_data
|
|
||||||
|
|
||||||
assert_equal [47, 17, 34], assigns[:data].values
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
require 'test_helper'
|
require 'test_helper'
|
||||||
|
|
||||||
|
# TODO: Add more detailed testing of the charts. There are previously defined tests in VCS before the Flash to Chart.js change.
|
||||||
class StatsControllerTest < ActionController::TestCase
|
class StatsControllerTest < ActionController::TestCase
|
||||||
|
|
||||||
def test_get_index_when_not_logged_in
|
def test_get_index_when_not_logged_in
|
||||||
|
|
@ -13,35 +14,6 @@ class StatsControllerTest < ActionController::TestCase
|
||||||
assert_response :success
|
assert_response :success
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_get_charts
|
|
||||||
login_as(:admin_user)
|
|
||||||
%w{
|
|
||||||
actions_done_last30days_data
|
|
||||||
actions_done_last12months_data
|
|
||||||
actions_completion_time_data
|
|
||||||
actions_visible_running_time_data
|
|
||||||
actions_running_time_data
|
|
||||||
actions_open_per_week_data
|
|
||||||
actions_day_of_week_all_data
|
|
||||||
actions_day_of_week_30days_data
|
|
||||||
actions_time_of_day_all_data
|
|
||||||
actions_time_of_day_30days_data
|
|
||||||
}.each do |action|
|
|
||||||
get action
|
|
||||||
assert_response :success
|
|
||||||
assert_template "stats/"+action
|
|
||||||
end
|
|
||||||
|
|
||||||
%w{
|
|
||||||
context_total_actions_data
|
|
||||||
context_running_actions_data
|
|
||||||
}.each do |action|
|
|
||||||
get action
|
|
||||||
assert_response :success
|
|
||||||
assert_template "stats/pie_chart_data"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_totals
|
def test_totals
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
get :index
|
get :index
|
||||||
|
|
@ -102,300 +74,6 @@ class StatsControllerTest < ActionController::TestCase
|
||||||
assert_response :success
|
assert_response :success
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_actions_done_last12months_data
|
|
||||||
travel_to Time.local(2013, 1, 15) do
|
|
||||||
login_as(:admin_user)
|
|
||||||
@current_user = User.find(users(:admin_user).id)
|
|
||||||
@current_user.todos.delete_all
|
|
||||||
|
|
||||||
given_todos_for_stats
|
|
||||||
|
|
||||||
# When I get the chart data
|
|
||||||
get :actions_done_last12months_data
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
# Then the todos for the chart should be retrieved
|
|
||||||
#assert_not_nil assigns['actions_done_last12months']
|
|
||||||
#assert_not_nil assigns['actions_created_last12months']
|
|
||||||
#assert_equal 7, assigns['actions_created_last12months'].count, "very old todo should not be retrieved"
|
|
||||||
|
|
||||||
# And they should be totalled in a hash
|
|
||||||
assert_equal 2, assigns['actions_created_last12months_array'][0], "there should be two todos in current month"
|
|
||||||
|
|
||||||
assert_equal 1, assigns['actions_created_last12months_array'][1], "there should be one todo in previous month"
|
|
||||||
assert_equal 1, assigns['actions_created_last12months_array'][2], "there should be one todo in two month ago"
|
|
||||||
assert_equal 1, assigns['actions_created_last12months_array'][3], "there should be one todo in three month ago"
|
|
||||||
assert_equal 2, assigns['actions_created_last12months_array'][4], "there should be two todos (1 created & 1 done) in four month ago"
|
|
||||||
|
|
||||||
assert_equal 1, assigns['actions_done_last12months_array'][1], "there should be one completed todo one-two months ago"
|
|
||||||
assert_equal 1, assigns['actions_done_last12months_array'][2], "there should be one completed todo two-three months ago"
|
|
||||||
assert_equal 1, assigns['actions_done_last12months_array'][4], "there should be one completed todo four-five months ago"
|
|
||||||
|
|
||||||
# And they should be averaged over three months
|
|
||||||
assert_equal 2/3.0, assigns['actions_done_avg_last12months_array'][1], "fourth month should be excluded"
|
|
||||||
assert_equal 2/3.0, assigns['actions_done_avg_last12months_array'][2], "fourth month should be included"
|
|
||||||
|
|
||||||
assert_equal (3)/3.0, assigns['actions_created_avg_last12months_array'][1], "one every month"
|
|
||||||
assert_equal (4)/3.0, assigns['actions_created_avg_last12months_array'][2], "two in fourth month"
|
|
||||||
|
|
||||||
# And the current month should be interpolated
|
|
||||||
fraction = Time.zone.now.day.to_f / Time.zone.now.end_of_month.day.to_f
|
|
||||||
assert_equal (2*(1/fraction)+2)/3.0, assigns['interpolated_actions_created_this_month'], "two this month and one in the last two months"
|
|
||||||
assert_equal (2)/3.0, assigns['interpolated_actions_done_this_month'], "none this month and one two the last two months"
|
|
||||||
|
|
||||||
# And totals should be calculated
|
|
||||||
assert_equal 2, assigns['max'], "max of created or completed todos in one month"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_empty_last12months_data
|
|
||||||
travel_to Time.local(2013, 1, 15) do
|
|
||||||
login_as(:admin_user)
|
|
||||||
@current_user = User.find(users(:admin_user).id)
|
|
||||||
@current_user.todos.delete_all
|
|
||||||
given_todos_for_stats
|
|
||||||
get :actions_done_last12months_data
|
|
||||||
assert_response :success
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_out_of_bounds_events_for_last12months_data
|
|
||||||
login_as(:admin_user)
|
|
||||||
@current_user = User.find(users(:admin_user).id)
|
|
||||||
@current_user.todos.delete_all
|
|
||||||
create_todo_in_past(2.years)
|
|
||||||
create_todo_in_past(15.months)
|
|
||||||
|
|
||||||
get :actions_done_last12months_data
|
|
||||||
assert_response :success
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_actions_done_last30days_data
|
|
||||||
login_as(:admin_user)
|
|
||||||
@current_user = User.find(users(:admin_user).id)
|
|
||||||
@current_user.todos.delete_all
|
|
||||||
|
|
||||||
given_todos_for_stats
|
|
||||||
|
|
||||||
# When I get the chart data
|
|
||||||
get :actions_done_last30days_data
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
# only tests relevant differences with actions_done_last_12months_data
|
|
||||||
|
|
||||||
assert_equal 31, assigns['actions_done_last30days_array'].size, "30 complete days plus 1 for the current day"
|
|
||||||
assert_equal 2, assigns['max'], "two actions created on one day is max"
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_actions_done_lastyears_data
|
|
||||||
login_as(:admin_user)
|
|
||||||
@current_user = User.find(users(:admin_user).id)
|
|
||||||
@current_user.todos.delete_all
|
|
||||||
|
|
||||||
given_todos_for_stats
|
|
||||||
|
|
||||||
# When I get the chart data
|
|
||||||
get :actions_done_lastyears_data
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
# only tests difference with actions_done_last_12months_data
|
|
||||||
|
|
||||||
# And the last two months are corrected
|
|
||||||
assert_equal 2/3.0, assigns['actions_done_avg_last_months_array'][23]
|
|
||||||
assert_equal 2/3.0, assigns['actions_done_avg_last_months_array'][24]
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_actions_completion_time_data
|
|
||||||
login_as(:admin_user)
|
|
||||||
@current_user = User.find(users(:admin_user).id)
|
|
||||||
@current_user.todos.delete_all
|
|
||||||
|
|
||||||
given_todos_for_stats
|
|
||||||
|
|
||||||
# When I get the chart data
|
|
||||||
get :actions_completion_time_data
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
# do not test stuff already implicitly tested in other tests
|
|
||||||
assert_equal 104, assigns['max_weeks'], "two years is 104 weeks (for completed_at)"
|
|
||||||
assert_equal 3, assigns['max_actions'], "3 completed within one week"
|
|
||||||
assert_equal 11, assigns['actions_completion_time_array'].size, "there should be 10 weeks of data + 1 for the rest"
|
|
||||||
assert_equal 1, assigns['actions_completion_time_array'][10], "there is one completed todo after the 10 weeks cut_off"
|
|
||||||
assert_equal 100.0, assigns['cum_percent_done'][10], "cumulative percentage should add up to 100%"
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_actions_running_time_data
|
|
||||||
login_as(:admin_user)
|
|
||||||
@current_user = User.find(users(:admin_user).id)
|
|
||||||
@current_user.todos.delete_all
|
|
||||||
|
|
||||||
given_todos_for_stats
|
|
||||||
|
|
||||||
# When I get the chart data
|
|
||||||
get :actions_running_time_data
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
# do not test stuff already implicitly tested in other tests
|
|
||||||
assert_equal 17, assigns['max_weeks'], "there are actions in the first 17 weeks of this year"
|
|
||||||
assert_equal 2, assigns['max_actions'], "2 actions running long together"
|
|
||||||
assert_equal 18, assigns['actions_running_time_array'].size, "there should be 17 weeks ( < cut_off) of data + 1 for the rest"
|
|
||||||
assert_equal 1, assigns['actions_running_time_array'][17], "there is one running todos in week 17 and zero after 17 weeks ( < cut off; ) "
|
|
||||||
assert_equal 100.0, assigns['cum_percent_done'][17], "cumulative percentage should add up to 100%"
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_actions_open_per_week_data
|
|
||||||
login_as(:admin_user)
|
|
||||||
@current_user = User.find(users(:admin_user).id)
|
|
||||||
@current_user.todos.delete_all
|
|
||||||
|
|
||||||
given_todos_for_stats
|
|
||||||
|
|
||||||
# When I get the chart data
|
|
||||||
get :actions_open_per_week_data
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
# do not test stuff already implicitly tested in other tests
|
|
||||||
assert_equal 17, assigns['max_weeks'], "there are actions in the first 17 weeks of this year"
|
|
||||||
assert_equal 4, assigns['max_actions'], "4 actions running together"
|
|
||||||
assert_equal 17, assigns['actions_open_per_week_array'].size, "there should be 17 weeks ( < cut_off) of data"
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_actions_visible_running_time_data
|
|
||||||
login_as(:admin_user)
|
|
||||||
@current_user = User.find(users(:admin_user).id)
|
|
||||||
@current_user.todos.delete_all
|
|
||||||
|
|
||||||
given_todos_for_stats
|
|
||||||
# Given todo1 is deferred (i.e. not visible)
|
|
||||||
@todo_today1.show_from = Time.zone.now + 1.week
|
|
||||||
@todo_today1.save
|
|
||||||
|
|
||||||
# When I get the chart data
|
|
||||||
get :actions_visible_running_time_data
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
# do not test stuff already implicitly tested in other tests
|
|
||||||
assert_equal 17, assigns['max_weeks'], "there are actions in the first 17 weeks of this year"
|
|
||||||
assert_equal 1, assigns['max_actions'], "1 action running long; 1 is deferred"
|
|
||||||
assert_equal 1, assigns['actions_running_time_array'][0], "there is one running todos and one deferred todo created in week 1"
|
|
||||||
assert_equal 18, assigns['actions_running_time_array'].size, "there should be 17 weeks ( < cut_off) of data + 1 for the rest"
|
|
||||||
assert_equal 1, assigns['actions_running_time_array'][17], "there is one running todos in week 17 and zero after 17 weeks ( < cut off; ) "
|
|
||||||
assert_equal 100.0, assigns['cum_percent_done'][17], "cumulative percentage should add up to 100%"
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_context_total_actions_data
|
|
||||||
login_as(:admin_user)
|
|
||||||
@current_user = User.find(users(:admin_user).id)
|
|
||||||
@current_user.todos.delete_all
|
|
||||||
|
|
||||||
given_todos_for_stats
|
|
||||||
|
|
||||||
# When I get the chart data
|
|
||||||
get :context_total_actions_data
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
assert_equal 9, assigns['data'].sum, "Nine todos in 1 context"
|
|
||||||
assert_equal 1, assigns['data'].values.size
|
|
||||||
|
|
||||||
# Given 10 more todos in 10 different contexts
|
|
||||||
1.upto(10) do |i|
|
|
||||||
context = @current_user.contexts.create!(:name => "context #{i}")
|
|
||||||
@current_user.todos.create!(:description => "created today with new context #{i}", :context => context)
|
|
||||||
end
|
|
||||||
|
|
||||||
# When I get the chart data
|
|
||||||
get :context_total_actions_data
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
assert_equal 19, assigns['data'].sum, "added 10 todos"
|
|
||||||
assert_equal 10, assigns['data'].values.size, "pie slices limited to max 10"
|
|
||||||
assert_equal 10, assigns['data'].values[9], "pie slices limited to max 10; last pie contains sum of rest (in percentage)"
|
|
||||||
assert_equal "(others)", assigns['data'].labels[9], "pie slices limited to max 10; last slice contains label for others"
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_context_running_actions_data
|
|
||||||
login_as(:admin_user)
|
|
||||||
@current_user = User.find(users(:admin_user).id)
|
|
||||||
@current_user.todos.delete_all
|
|
||||||
|
|
||||||
given_todos_for_stats
|
|
||||||
|
|
||||||
# When I get the chart data
|
|
||||||
get :context_running_actions_data
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
assert_equal 4, assigns['data'].sum, "Four todos in 1 context"
|
|
||||||
assert_equal 1, assigns['data'].values.size
|
|
||||||
|
|
||||||
# Given 10 more todos in 10 different contexts
|
|
||||||
1.upto(10) do |i|
|
|
||||||
context = @current_user.contexts.create!(:name => "context #{i}")
|
|
||||||
@current_user.todos.create!(:description => "created today with new context #{i}", :context => context)
|
|
||||||
end
|
|
||||||
|
|
||||||
# When I get the chart data
|
|
||||||
get :context_running_actions_data
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
assert_equal 10, assigns['data'].values.size, "pie slices limited to max 10"
|
|
||||||
assert_equal 14, assigns['data'].values[9], "pie slices limited to max 10; last pie contains sum of rest (in percentage)"
|
|
||||||
assert_equal "(others)", assigns['data'].labels[9], "pie slices limited to max 10; last slice contains label for others"
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_actions_day_of_week_all_data
|
|
||||||
login_as(:admin_user)
|
|
||||||
@current_user = User.find(users(:admin_user).id)
|
|
||||||
@current_user.todos.delete_all
|
|
||||||
|
|
||||||
given_todos_for_stats
|
|
||||||
|
|
||||||
# When I get the chart data
|
|
||||||
get :actions_day_of_week_all_data
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
# FIXME: testdata is relative from today, so not stable to test on day_of_week
|
|
||||||
# trivial not_nil tests
|
|
||||||
assert_not_nil assigns['max']
|
|
||||||
assert_not_nil assigns['actions_creation_day_array']
|
|
||||||
assert_not_nil assigns['actions_completion_day_array']
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_actions_day_of_week_30days_data
|
|
||||||
login_as(:admin_user)
|
|
||||||
@current_user = User.find(users(:admin_user).id)
|
|
||||||
@current_user.todos.delete_all
|
|
||||||
|
|
||||||
given_todos_for_stats
|
|
||||||
|
|
||||||
# When I get the chart data
|
|
||||||
get :actions_day_of_week_30days_data
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
# FIXME: testdata is relative from today, so not stable to test on day_of_week
|
|
||||||
# trivial not_nil tests
|
|
||||||
assert_not_nil assigns['max']
|
|
||||||
assert_not_nil assigns['actions_creation_day_array']
|
|
||||||
assert_not_nil assigns['actions_completion_day_array']
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_actions_time_of_day_all_data
|
|
||||||
login_as(:admin_user)
|
|
||||||
@current_user = User.find(users(:admin_user).id)
|
|
||||||
@current_user.todos.delete_all
|
|
||||||
|
|
||||||
given_todos_for_stats
|
|
||||||
|
|
||||||
# When I get the chart data
|
|
||||||
get :actions_time_of_day_all_data
|
|
||||||
assert_response :success
|
|
||||||
|
|
||||||
# FIXME: testdata is relative from today, so not stable to test on day_of_week
|
|
||||||
# for now just trivial not_nil tests
|
|
||||||
assert_not_nil assigns['max']
|
|
||||||
assert_not_nil assigns['actions_creation_hour_array']
|
|
||||||
assert_not_nil assigns['actions_completion_hour_array']
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_show_selected_actions_from_chart_avrt
|
def test_show_selected_actions_from_chart_avrt
|
||||||
login_as(:admin_user)
|
login_as(:admin_user)
|
||||||
@current_user = User.find(users(:admin_user).id)
|
@current_user = User.find(users(:admin_user).id)
|
||||||
|
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
require 'minimal_test_helper'
|
|
||||||
require 'app/models/stats/pie_chart_data'
|
|
||||||
require 'active_support/core_ext/string'
|
|
||||||
|
|
||||||
class Stats::PieChartDataTest < Minitest::Test
|
|
||||||
|
|
||||||
def test_with_0_items
|
|
||||||
data = Stats::PieChartData.new([], 'a chart', 50)
|
|
||||||
|
|
||||||
assert_equal [], data.values
|
|
||||||
assert_equal [], data.labels
|
|
||||||
assert_equal [], data.ids
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_with_less_than_10_items
|
|
||||||
items = [
|
|
||||||
{'id' => 1, 'name' => 'one', 'total' => 11},
|
|
||||||
{'id' => 2, 'name' => 'two', 'total' => 4},
|
|
||||||
{'id' => 3, 'name' => 'three', 'total' => 8},
|
|
||||||
{'id' => 4, 'name' => 'four', 'total' => 13},
|
|
||||||
{'id' => 5, 'name' => 'five', 'total' => 20},
|
|
||||||
{'id' => 6, 'name' => 'six', 'total' => 17},
|
|
||||||
{'id' => 7, 'name' => 'seven', 'total' => 5},
|
|
||||||
{'id' => 8, 'name' => 'eight', 'total' => 1},
|
|
||||||
{'id' => 9, 'name' => 'nine', 'total' => 6}
|
|
||||||
]
|
|
||||||
|
|
||||||
data = Stats::PieChartData.new(items, 'a chart', 50)
|
|
||||||
|
|
||||||
assert_equal [12, 4, 9, 15, 23, 20, 5, 1, 7], data.values
|
|
||||||
assert_equal ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine"], data.labels
|
|
||||||
assert_equal [1, 2, 3, 4, 5, 6, 7, 8, 9], data.ids
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_with_exactly_10_items
|
|
||||||
items = [
|
|
||||||
{'id' => 1, 'name' => 'one', 'total' => 11},
|
|
||||||
{'id' => 2, 'name' => 'two', 'total' => 4},
|
|
||||||
{'id' => 3, 'name' => 'three', 'total' => 8},
|
|
||||||
{'id' => 4, 'name' => 'four', 'total' => 13},
|
|
||||||
{'id' => 5, 'name' => 'five', 'total' => 20},
|
|
||||||
{'id' => 6, 'name' => 'six', 'total' => 17},
|
|
||||||
{'id' => 7, 'name' => 'seven', 'total' => 5},
|
|
||||||
{'id' => 8, 'name' => 'eight', 'total' => 1},
|
|
||||||
{'id' => 9, 'name' => 'nine', 'total' => 6},
|
|
||||||
{'id' => 10, 'name' => 'ten', 'total' => 19}
|
|
||||||
]
|
|
||||||
|
|
||||||
data = Stats::PieChartData.new(items, 'a chart', 50)
|
|
||||||
|
|
||||||
assert_equal [10, 3, 7, 12, 19, 16, 4, 0, 5, 18], data.values
|
|
||||||
assert_equal ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"], data.labels
|
|
||||||
assert_equal [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], data.ids
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_with_more_than_10_items
|
|
||||||
items = [
|
|
||||||
{'id' => 1, 'name' => 'one', 'total' => 11},
|
|
||||||
{'id' => 2, 'name' => 'two', 'total' => 4},
|
|
||||||
{'id' => 3, 'name' => 'three', 'total' => 8},
|
|
||||||
{'id' => 4, 'name' => 'four', 'total' => 13},
|
|
||||||
{'id' => 5, 'name' => 'five', 'total' => 20},
|
|
||||||
{'id' => 6, 'name' => 'six', 'total' => 17},
|
|
||||||
{'id' => 7, 'name' => 'seven', 'total' => 5},
|
|
||||||
{'id' => 8, 'name' => 'eight', 'total' => 1},
|
|
||||||
{'id' => 9, 'name' => 'nine', 'total' => 6},
|
|
||||||
{'id' => 10, 'name' => 'ten', 'total' => 19},
|
|
||||||
{'id' => 11, 'name' => 'eleven', 'total' => 14}
|
|
||||||
]
|
|
||||||
|
|
||||||
data = Stats::PieChartData.new(items, 'a chart', 50)
|
|
||||||
|
|
||||||
assert_equal [9, 3, 6, 11, 16, 14, 4, 0, 5, 27], data.values
|
|
||||||
assert_equal ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "(others)"], data.labels
|
|
||||||
assert_equal [1, 2, 3, 4, 5, 6, 7, 8, 9, -1], data.ids
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue