require "active_support/all" module AdminData module Analytics # a utility class to handle date interpolation for different databases class Dater attr_accessor :adapter, :type def initialize(adapter, type = 'daily') @adapter = adapter @type = type end def date_select_key "date_data" end def group_by_key if adapter =~ /postgresql/i self.type == 'monthly' ? "date_part('year', created_at), date_part('month', created_at)" : "date_data" elsif adapter =~ /mysql/i self.type == 'monthly' ? "YEAR(created_at), MONTH(created_at)" : "date_data" else self.type == 'monthly' ? "strftime('%Y', created_at), strftime('%m', created_at)" : "date_data" end end def count_select_key "count_data" end def count_function "count(*) as count_data" end def date_select_function self.type == 'monthly' ? date_select_function_monthly : date_select_function_daily end private def date_select_function_monthly if adapter =~ /mysql/i "MONTH(created_at) as date_data" elsif adapter =~ /postgresql/i "date_part('month', created_at) as date_data" else "strftime('%m', created_at) as date_data" end end def date_select_function_daily if adapter =~ /mysql/i "date_format(created_at, '%Y-%m-%d') as date_data" else "date(created_at) as date_data" end end end def self.monthly_report(klass, end_date) begin_date = end_date.ago(1.year) raise "begin_date should not be after end_date" if begin_date > end_date raise AdminData::NoCreatedAtColumnException unless klass.columns.find {|r| r.name == 'created_at'} begin_date = begin_date.beginning_of_day end_date = end_date.end_of_day dater = Dater.new(ActiveRecord::Base.connection.adapter_name, 'monthly') query = klass.unscoped query = query.where(["created_at >= ?", begin_date]) query = query.where(["created_at <= ?", end_date]) query = query.group(dater.group_by_key) query = query.select(dater.date_select_function) query = query.select(dater.count_function) debug "sql: " + query.to_sql result = query.all debug "sql result: #{result.inspect}" extract_data_from_result_set_monthly(result, dater, begin_date, end_date) end def self.extract_data_from_result_set_monthly(result, dater, begin_date, end_date) debug "extracting from result set now" result_hash = {} result.each do |record| result_hash.merge!(record[dater.date_select_key].to_i => record[dater.count_select_key]) end debug "result_hash.inspect is #{result_hash.inspect}" current_month = begin_date.strftime('%m').to_i months_order = (1..current_month).to_a.reverse + (current_month..12).to_a.reverse months_order.uniq!.reverse! debug "months order is #{months_order.inspect}" final_hash = ActiveSupport::OrderedHash.new months_order.each do |month| month = month.to_i #sqlite3 has months as 03 instead of 3 m = Time.now.change(:month => month) m = m.ago(1.year) if month > current_month key = m.strftime('%b-%Y') key = "'#{key}'" if dater.adapter =~ /postgresql/i value = result_hash[month] elsif dater.adapter =~ /mysql/i value = result_hash[month] else value = result_hash[month] end value = value.to_i debug "month: #{month} key: #{key} value: #{value}" final_hash.merge!(key => value) end final_hash.to_a.tap {|e| debug e.inspect } end def self.daily_report(klass, end_date) begin_date = end_date.ago(1.month) raise "begin_date should not be after end_date" if begin_date > end_date raise AdminData::NoCreatedAtColumnException unless klass.columns.find {|r| r.name == 'created_at'} begin_date = begin_date.beginning_of_day end_date = end_date.end_of_day dater = Dater.new(ActiveRecord::Base.connection.adapter_name) query = klass.unscoped query = query.where(["created_at >= ?", begin_date]) query = query.where(["created_at <= ?", end_date]) query = query.group(dater.group_by_key) query = query.select(dater.date_select_function) query = query.select(dater.count_function) debug "sql: " + query.to_sql result = query.all debug "sql result: #{result.inspect}" extract_data_from_result_set_daily(result, dater, begin_date, end_date).tap {|e| debug "formatted output: "+e.inspect} end def self.extract_data_from_result_set_daily(result, dater, begin_date, end_date) count = result.map {|r| r[dater.count_select_key] } dates = result.map {|r| r[dater.date_select_key] } debug "count is: #{count.inspect}" debug "dates is: #{dates.inspect}" final_output= [] while(begin_date) do s = begin_date.strftime('%Y-%m-%d') final_count = if index = dates.index(s) count[index].to_i else 0 end final_output << ["'#{s}'", final_count] begin_date = begin_date.tomorrow break if begin_date > end_date end final_output end def self.debug(msg) puts msg end end end