Added the fantastic statistics work contributed by lrbalt! This is a work in progress. It's not rendering correctly for me in Firefox but is OK in Safari and there is at least one other minor error. Contribute bug reports and fixes to ticket #406.

git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@587 a4c988fc-2ded-0310-b66e-134b36920a42
This commit is contained in:
lukemelia 2007-09-13 03:21:37 +00:00
parent 4f3762a64c
commit 8c26ea7cb5
26 changed files with 1013 additions and 0 deletions

View file

@ -0,0 +1,516 @@
class StatsController < ApplicationController
def index
@page_title = 'TRACKS::Statistics'
@projects = @user.projects
@contexts = @user.contexts
@actions = @user.todos
@tags = @user.tags
@unique_tags = @tags.find(:all, {:group=>"tag_id"})
@hidden_contexts = @contexts.select{ |c| c.hide? }
@first_action = @actions.find(:first, :order => "created_at asc")
# default chart dimensions
@chart_width=450
@chart_height=250
@pie_width=@chart_width
@pie_height=325
get_stats_actions
get_stats_contexts
get_stats_projects
get_stats_tags
render :layout => 'standard'
end
def actions_done_last12months_data
@actions = @user.todos
@actions_done_last12months = @actions.count(
:all, {
:group => "period_diff(extract(year_month from now()), extract(year_month from completed_at))",
:conditions => "period_diff(extract(year_month from now()), extract(year_month from completed_at)) <= 12 and not completed_at is null"
})
@actions_created_last12months = @actions.count(:all, {
:group => "period_diff(extract(year_month from now()), extract(year_month from created_at))",
:conditions => "period_diff(extract(year_month from now()), extract(year_month from created_at)) <= 12"
})
# find max count for graph
@max=0
# convert to hash to be able to fill in non-existing days in @actions_done_last12months
@sum_actions_done_last12months=0
@actions_done_last12months_hash = Hash.new(0)
@actions_done_last12months.each do |month, count|
@actions_done_last12months_hash[month] = count
@sum_actions_done_last12months+= count.to_i
if count.to_i > @max
@max = count.to_i
end
end
@sum_actions_created_last12months=0
@actions_created_last12months_hash = Hash.new(0)
@actions_created_last12months.each do |month, count|
@actions_created_last12months_hash[month] = count
@sum_actions_created_last12months+= count.to_i
if count.to_i > @max
@max = count.to_i
end
end
render :layout => false
end
def actions_done_last30days_data
@actions = @user.todos
# get count of actions done in the past 30 days. Results in a array of arrays
@actions_done_last30days = @actions.count(:all, {
:group => "datediff(now(), completed_at)",
:conditions => "datediff(now(), completed_at) <= 30 and not completed_at is null"
})
@actions_created_last30days = @actions.count(:all, {
:group => "datediff(now(), created_at)",
:conditions => "datediff(now(), created_at) <= 30"
})
@max=0
# convert to hash to be albe to fill in non-existing days in @actions_done_last30days
@sum_actions_done_last30days=0
@actions_done_last30days_hash = Hash.new(0)
@actions_done_last30days.each do |day, count|
@actions_done_last30days_hash[day] = count
@sum_actions_done_last30days+= count.to_i
if count.to_i > @max
@max = count.to_i
end
end
@sum_actions_created_last30days=0
# convert to hash to be albe to fill in non-existing days in @actions_done_last30days
@actions_created_last30days_hash = Hash.new(0)
@actions_created_last30days.each do |day, count|
@actions_created_last30days_hash[day] = count
@sum_actions_created_last30days+= count.to_i
if count.to_i > @max
@max = count.to_i
end
end
render :layout => false
end
def actions_completion_time_data
@actions = @user.todos
@actions_completion_time = @actions.count(:all, {
:group => "datediff(completed_at, created_at)",
:conditions => "not completed_at is null", :order => "datediff(completed_at, created_at) ASC"
})
# convert to hash to be able to fill in non-existing days in @actions_completion_time
# also convert days to weeks (/7)
@max_days=0
@max_actions=0
@actions_completion_time_hash = Hash.new(0)
@actions_completion_time.each do |days, total|
# RAILS_DEFAULT_LOGGER.error("\n" + total.to_s + " - " + days + "\n")
@actions_completion_time_hash[days.to_i/7] = @actions_completion_time_hash[days.to_i/7] + total
if days.to_i > @max_days
@max_days=days.to_i
end
if @actions_completion_time_hash[days.to_i/7] > @max_actions
@max_actions = @actions_completion_time_hash[days.to_i/7]
end
end
@cut_off = 10
render :layout => false
end
def actions_running_time_data
@actions = @user.todos
@actions_running_time = @actions.count(:all, {
:group => "datediff(now(), created_at)",
:conditions => "completed_at is null", :order => "datediff(now(), created_at) ASC"
})
# convert to hash to be able to fill in non-existing days in @actions_running_time
# also convert days to weeks (/7)
@max_days=0
@max_actions=0
@actions_running_time_hash = Hash.new(0)
@actions_running_time.each do |days, total|
# RAILS_DEFAULT_LOGGER.error("\n" + total.to_s + " - " + days + "\n")
@actions_running_time_hash[days.to_i/7] = @actions_running_time_hash[days.to_i/7] + total
if days.to_i > @max_days
@max_days=days.to_i
end
if @actions_running_time_hash[days.to_i/7] > @max_actions
@max_actions = @actions_running_time_hash[days.to_i/7]
end
end
# cut off chart at 52 weeks = one year
@cut_off=52
render :layout => false
end
def actions_visible_running_time_data
@actions = @user.todos
@actions_running_time = @actions.count(:all, {
:group => "datediff(now(), created_at)",
:conditions => "completed_at is null", :order => "datediff(now(), created_at) ASC"
})
@actions_running_time = @actions.find_by_sql(
"SELECT datediff(now(), t.created_at) as days, count(*) as total "+
"FROM todos t LEFT OUTER JOIN projects p ON t.project_id = p.id LEFT OUTER JOIN contexts c ON t.context_id = c.id "+
"WHERE t.user_id="+@user.id.to_s+" "+
"AND t.completed_at is null " +
"AND NOT (p.state='hidden' OR c.hide=1) " +
"GROUP BY days ORDER BY days DESC"
)
# convert to hash to be able to fill in non-existing days in @actions_running_time
# also convert days to weeks (/7)
@max_days=0
@max_actions=0
@actions_running_time_hash = Hash.new(0)
@actions_running_time.each do |a|
# RAILS_DEFAULT_LOGGER.error("\n" + total.to_s + " - " + days + "\n")
@actions_running_time_hash[a.days.to_i/7] += a.total.to_i
if a.days.to_i > @max_days
@max_days=a.days.to_i
end
if @actions_running_time_hash[a.days.to_i/7] > @max_actions
@max_actions = @actions_running_time_hash[a.days.to_i/7]
end
end
# cut off chart at 52 weeks = one year
@cut_off=52
render :layout => false
end
def context_total_actions_data
@contexts = @user.contexts
# SELECT c.name, c.hide, count(*) as totaal
# FROM contexts c, todos t
# where t.context_id=c.id group by c.id order by totaal desc;
# get total action count per context
@actions_per_context = @contexts.find_by_sql(
"SELECT c.name as name, count(*) as total "+
"FROM contexts c, todos t "+
"WHERE t.context_id=c.id "+
"AND t.user_id="+@user.id.to_s+" "+
"GROUP BY c.id ORDER BY total DESC"
)
@sum=0
0.upto @actions_per_context.size()-1 do |i|
@sum += @actions_per_context[i]['total'].to_i
end
render :layout => false
end
def context_running_actions_data
@contexts = @user.contexts
# SELECT c.name, c.hide, count(*) as totaal
# FROM contexts c, todos t
# where t.context_id=c.id group by c.id order by totaal desc;
# get uncompleted action count per visible context
@actions_per_context = @contexts.find_by_sql(
"SELECT c.name as name, count(*) as total "+
"FROM contexts c, todos t "+
"WHERE t.context_id=c.id AND t.completed_at IS NULL AND NOT c.hide "+
"AND t.user_id="+@user.id.to_s+" "+
"GROUP BY c.id ORDER BY total DESC"
)
@sum=0
0.upto @actions_per_context.size()-1 do |i|
@sum += @actions_per_context[i]['total'].to_i
end
render :layout => false
end
def actions_day_of_week_all_data
@actions = @user.todos
@actions_creation_day = @actions.count(:all, {:group => "dayofweek(created_at)" })
@actions_completion_day = @actions.count(:all, {:group => "dayofweek(completed_at)", :conditions => "not completed_at is null" })
# 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 do |dayofweek, total|
# dayofweek: sunday=1..saterday=7
@max = total.to_i > @max ? total.to_i : @max
@actions_creation_day_array[dayofweek.to_i-1]=total.to_i
end
# 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 do |dayofweek, total|
# dayofweek: sunday=1..saterday=7
@max = total.to_i > @max ? total.to_i : @max
@actions_completion_day_array[dayofweek.to_i-1]=total.to_i
end
render :layout => false
end
def actions_day_of_week_30days_data
@actions = @user.todos
@actions_creation_day = @actions.count(
:all, {
:group => "dayofweek(created_at)",
:conditions => "datediff(now(), created_at) <= 30"
})
@actions_completion_day = @actions.count(
:all, {:group => "dayofweek(completed_at)",
:conditions => "not completed_at is null and datediff(now(), created_at) <= 30"
})
# 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 do |dayofweek, total|
# dayofweek: sunday=1..saterday=7
@max = total.to_i > @max ? total.to_i : @max
@actions_creation_day_array[dayofweek.to_i-1]=total.to_i
end
# 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 do |dayofweek, total|
# dayofweek: sunday=1..saterday=7
@max = total.to_i > @max ? total.to_i : @max
@actions_completion_day_array[dayofweek.to_i-1]=total.to_i
end
render :layout => false
end
def actions_time_of_day_all_data
@actions = @user.todos
@actions_creation_hour = @actions.count(:all, {:group => "hour(convert_tz(created_at, @@session.time_zone, '+2:00'))" })
@actions_completion_hour = @actions.count(:all, {:group => "hour(convert_tz(created_at, @@session.time_zone, '+2:00'))", :conditions => "not completed_at is null" })
# convert to hash to be able to fill in non-existing days
@max=0
@actions_creation_hour_array = Array.new(24) { |i| 0}
@actions_creation_hour.each do |hour, total|
@max = total.to_i > @max ? total.to_i : @max
@actions_creation_hour_array[hour.to_i]=total.to_i
end
# 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 do |hour, total|
@max = total.to_i > @max ? total.to_i : @max
@actions_completion_hour_array[hour.to_i]=total.to_i
end
render :layout => false
end
def actions_time_of_day_30days_data
@actions = @user.todos
# TODO: find out how to find current timezone
@actions_creation_hour = @actions.count(
:all, {
:group => "hour(convert_tz(created_at, @@session.time_zone, '+2:00'))",
:conditions => "datediff(now(), created_at) <= 30"
})
# TODO: find out how to find current timezone
@actions_completion_hour = @actions.count(
:all, {:group => "hour(convert_tz(completed_at, @@session.time_zone, '+2:00'))",
:conditions => "not completed_at is null and datediff(now(), completed_at) <= 30"
})
# convert to hash to be able to fill in non-existing days
@max=0
@actions_creation_hour_array = Array.new(24) { |i| 0}
@actions_creation_hour.each do |hour, total|
@max = total.to_i > @max ? total.to_i : @max
@actions_creation_hour_array[hour.to_i]=total.to_i
end
# 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 do |hour, total|
@max = total.to_i > @max ? total.to_i : @max
@actions_completion_hour_array[hour.to_i]=total.to_i
end
render :layout => false
end
private
def get_stats_actions
# time to complete
@actions_avg_ttc = @actions.average("datediff(completed_at, created_at)", {:conditions => "not completed_at is null"} )
@actions_max_ttc = @actions.maximum("datediff(completed_at, created_at)", {:conditions => "not completed_at is null"} )
@actions_min_ttc = @actions.minimum("datediff(completed_at, created_at)", {:conditions => "not completed_at is null"} )
@actions_min_ttc_sec = @actions.minimum("timediff(completed_at, created_at)", {:conditions => "not completed_at is null"} )
# get count of actions created and actions done in the past 30 days. Results in a array of arrays
@actions_done_last30days = @actions.count(:all, {
:group => "datediff(now(), completed_at)",
:conditions => "datediff(now(), completed_at) <= 30"
})
@actions_created_last30days = @actions.count(:all, {
:group => "datediff(now(), created_at)",
:conditions => "datediff(now(), created_at) <= 30"
})
@sum_actions_done_last30days=0
# convert to hash to be albe to fill in non-existing days in @actions_done_last30days
@actions_done_last30days_hash = Hash.new(0)
@actions_done_last30days.each do |day, count|
@actions_done_last30days_hash[day] = count
@sum_actions_done_last30days+= count.to_i
end
@sum_actions_created_last30days=0
# convert to hash to be albe to fill in non-existing days in @actions_done_last30days
@actions_created_last30days_hash = Hash.new(0)
@actions_created_last30days.each do |day, count|
@actions_created_last30days_hash[day] = count
@sum_actions_created_last30days+= count.to_i
end
# get count of actions done in the past 12 months. Results in a array of arrays
@actions_done_last12months = @actions.count(:all, {
:group => "period_diff(extract(year_month from now()), extract(year_month from completed_at))",
:conditions => "period_diff(extract(year_month from now()), extract(year_month from completed_at)) <= 12 and not completed_at is null"
})
@actions_created_last12months = @actions.count(:all, {
:group => "period_diff(extract(year_month from now()), extract(year_month from created_at))",
:conditions => "period_diff(extract(year_month from now()), extract(year_month from created_at)) <= 12"
})
# convert to hash to be albe to fill in non-existing days in @actions_done_last12months
@sum_actions_done_last12months=0
@actions_done_last12months_hash = Hash.new(0)
@actions_done_last12months.each do |month, count|
@actions_done_last12months_hash[month] = count
@sum_actions_done_last12months+= count.to_i
end
@sum_actions_created_last12months=0
@actions_created_last12months_hash = Hash.new(0)
@actions_created_last12months.each do |month, count|
@actions_created_last12months_hash[month] = count
@sum_actions_created_last12months+= count.to_i
end
end
def get_stats_contexts
# get action count per context for TOP 5
@actions_per_context = @contexts.find_by_sql(
"SELECT c.name as name, count(*) as total "+
"FROM contexts c, todos t "+
"WHERE t.context_id=c.id "+
"AND t.user_id="+@user.id.to_s+" "+
"GROUP BY c.id ORDER BY total DESC " +
"LIMIT 5"
)
# get uncompleted action count per visible context for TOP 5
@running_actions_per_context = @contexts.find_by_sql(
"SELECT c.name as name, count(*) as total "+
"FROM contexts c, todos t "+
"WHERE t.context_id=c.id AND t.completed_at IS NULL AND NOT c.hide "+
"AND t.user_id="+@user.id.to_s+" "+
"GROUP BY c.id ORDER BY total DESC " +
"LIMIT 5"
)
end
def get_stats_projects
# get the first 10 projects and their action count (all actions)
@projects_and_actions = @projects.find_by_sql(
"SELECT p.name, count(*) as count "+
"FROM projects p, todos t "+
"WHERE p.id = t.project_id "+
"AND p.user_id="+@user.id.to_s+" "+
"GROUP BY p.id "+
"ORDER BY count DESC " +
"LIMIT 10"
)
# get the first 10 projects with their actions count of actions that
# have been created or completed the past 30 days
@projects_and_actions_last30days = @projects.find_by_sql(
"SELECT p.name, count(*) AS count "+
"FROM todos t, projects p "+
"WHERE t.project_id = p.id AND "+
" (datediff(now(), t.created_at) < 30 OR "+
" datediff(now(), t.completed_at) < 30) "+
"AND p.user_id="+@user.id.to_s+" "+
"GROUP BY p.id "+
"ORDER BY count DESC"
)
# get the first 10 projects and their running time (creation date versus now())
@projects_and_runtime = @projects.find_by_sql(
"SELECT name, datediff(now(),created_at) AS days "+
"FROM projects p "+
"WHERE state='active' "+
"AND p.user_id="+@user.id.to_s+" "+
"ORDER BY days DESC "+
"LIMIT 10"
)
end
def get_stats_tags
# todo: parameterize limit
query = "select tags.id, name, count(*) as count"
query << " from taggings, tags"
query << " where tags.id = tag_id"
query << " AND taggings.user_id="+@user.id.to_s+" "
query << " group by tag_id"
query << " order by count DESC, name"
query << " limit 100"
@tags_for_cloud = Tag.find_by_sql(query).sort_by { |tag| tag.name.downcase }
max, @tags_min = 0, 0
@tags_for_cloud.each { |t|
max = t.count.to_i if t.count.to_i > max
@tags_min = t.count.to_i if t.count.to_i < @tags_min
}
# 10 = number of levels
@tags_divisor = ((max - @tags_min) / 10) + 1
end
end