table look confusion - ruby-on-rails

I have this problem. i think it is simple but i just couldn't get it right.
Im building graph and table using gruff.I have a x axis scaled from month 01 to 12
The data shud hv bn plotted in month 09 and 10 but it is plotted to month 08 and 10.
def stat1
#yeara = params[:year]
#games = Games.find(:all)
g = Gruff::StackedBar.new('800x450')
g.theme = {
:colors => ['#138F6A','#330000','#ff6600', '#3bb000', '#1e90ff', '#efba00', '#0aaafd'],
:marker_color => '#aaa',
:background_colors => ['#eaeaea', '#fff']
}
g.hide_title = true
#dr = Game.count(:all, :conditions=> ["game_id= ? and DATE_FORMAT(date, '%Y')= ?",1,#yeara], :group => "DATE_FORMAT(date, '%m')", :order =>"date ASC")
#df = Game.count(:all, :conditions=> ["game_id= ? and DATE_FORMAT(date, '%Y') = ?",2,#yeara], :group => "DATE_FORMAT(date, '%m')", :order =>"date ASC")
# all 12 month a year
#full = Hash["01",0,"02",0,"03",0,"04",0,"05",0,"06",0,"07",0,"08",0,"09",0,"10",0,"11",0,"12",0]
year = (#dr.keys |#df.keys|#full.keys).sort
#keys = Hash[*year.collect {|v| [year.index(v),v.to_s] }.flatten]
# Plot data to table
#dfdr = Array.new
#dfdr << #keys.collect {|k,v| #df[v].nil? ? 0 : #df[v]}
#dfdr << #keys.collect {|k,v| #dr[v].nil? ? 0 : #dr[v]}
# Plot data to graph
g.data("Adam", #keys.collect {|k,v| #dr[v].nil? ? 0 : #dr[v]})
g.data("Eve", #keys.collect {|k,v| #df[v].nil? ? 0 : #df[v]})
g.labels = #keys
g.write("#{RAILS_ROOT}/public/images/game.png")
render(:layout => false)
here are some explanation
the output for
#dr = Game.count(:all, :conditions=> ["game_id= ? and DATE_FORMAT(date, '%Y')= ?",1,#yeara], :group => "DATE_FORMAT(date, '%m')", :order =>"date ASC")
=> [["09",3],["10",1]
#df = Game.count(:all, :conditions=> ["game_id= ? and DATE_FORMAT(date, '%Y') = ?",2,#yeara], :group => "DATE_FORMAT(date, '%m')", :order =>"date ASC")
=> [["10",2]]
trying to trace where went wrong..but ran out of idea.
please point something out to me.wish i could paste the graph but im not eligible yet say stackoverflow. ;)
thank u

OK got it.
#keys.collect {|k,v| #df[v].nil? ? 0 : #df[v]
the #keys are in unsorted order.So simply sort it first.
#keys.sort.collect {|k,v| #df[v].nil? ? 0 : #df[v]
Thank you all. :))))) Sorry if anything. ;)

Related

rails 3 sqlite pass array in query via placeholder

How can i give an array as a placeholder without sqlite seeing as 1 value but several values that are in the array
value = Array.new
value.push(broadcast_date_from)
value.push(broadcast_date_to)
puts value #["a", "2006-01-02 00:00", "2006-01-02 23:59"]
find(:all, :order => 'broadcast_date', :conditions => ['name LIKE ? and broadcast_date >= ? and broadcast_date <= ?', name, #value ])
But i get this error:
wrong number of bind variables (1 for 3) in: name LIKE ? and broadcast_date >= ? and broadcast_date <= ?
Is there anyway to make it see 3 values in the array and not 1.
You need to add the splat operator * before you call your array:
values = ['condition for name']
values.push(broadcast_date_from)
values.push(broadcast_date_to)
find(:all, :order => 'broadcast_date', :conditions => ['name LIKE ? and broadcast_date >= ? and broadcast_date <= ?', *values ])
Small article about the splat operator: http://theplana.wordpress.com/2007/03/03/ruby-idioms-the-splat-operator/
Improvement for you: use .where() instead of .find()
First, the excellent guide about it: http://guides.rubyonrails.org/active_record_querying.html#conditions
Then, a little example to show the benefits of the where:
class User < ActiveRecord::Base
def get_posts(options = {})
str_conditions = ['user_id = ?']
args_conditions = [self.id]
if options.has_key?(:active)
str_conditions << 'active = ?'
args_conditions << options[:active]
end
if options.has_key?(:after)
str_conditions << 'created_at >= ?'
args_conditions << options[:after]
end
if options.has_key?(:limit)
Post.find(:conditions => [str_conditions.join(' OR '), *args_conditions], :limit => options[:limit])
else
Post.find(:conditions => [str_conditions.join(' OR '), *args_conditions])
end
end
Different usages:
user = User.first
user.get_posts(:active => true, :after => Date.today, :limit => 10)
user.get_posts
The same method, but using the where method (very nice for chain-scoping):
def get_posts(options = {})
scope = self.posts
scope = scope.where(active: options[:active]) if options.has_key?(:active)
scope = scope.where('created_at >= ?', options[:after]) if options.has_key?(:after)
scope = scope.limit(options[:limit]) if options.has_key?(:limit)
return scope
end
Keep in mind that you can chain scope with the .where method:
User.where(active: true).where('created_at < ?', Date.today-1.weeks).includes(:posts).where(posts: { name: "Name of a specific post" })

Are there any model analytics gems?

I'm working on allowing clients to view analytics per day, week, month, in a period of time, grouped by hours or days or months, etc... All of that is based on the created_at attribute.
Is there any gem out there that already does this? Something like:
Posts.analytics(:by => :day, :period => :this_week, :column => :created_at)
Would return:
{
'2012-06-19' => 14,
'2012-06-20' => 0, // Empty rows padding support*
'2012-06-21' => 3
}
I'm trying to make it from scratch but it seems like a lot of unecessary work if there's already a gem to do the job.
Update
I tried to make an analytics module that gets included into all models for easy analytics generation, But it's really unreliable, Sometimed i get more days than i need, and it's really messy, Could anyone collaborate and rewrite/improve on this:
# Usage:
# include Analytics::Timeline
# Model.timeline(:period => :last_24_hours, :time_by => :hour)
module Analytics
module Timeline
def self.included(base)
base.class_eval {
def self.timeline(*filters)
filters = filters[0]
period = filters[:period] || :this_week
time_by = filters[:time_by] || :days
date_column = filters[:date_column] || :created_at
# Named periods conventions
period_range = case period
when :last_12_hours
[Time.now-12.hours, Time.now]
when :last_24_hours
[Time.now-24.hours, Time.now]
when :last_7_days
[Time.now-7.days, Time.now]
when :last_30_days
[Time.now-30.days, Time.now]
when :this_week
[Time.now.beginning_of_week, Time.now.end_of_week]
when :past_week
[(Time.now - 1.week).beginning_of_week, (Time.now - 1.week).end_of_week]
when :this_month
[Time.now.beginning_of_month, Time.now.end_of_month]
when :past_month
[(Time.now-1.month).beginning_of_month, (Time.now - 1.month).end_of_month]
when :this_year
[Time.now.beginning_of_year, Time.now.end_of_year]
end
period_range = period if period.kind_of?(Array)
period_range = [period, Time.now] if period.is_a?(String)
# determine the SQL group method
group_column = case time_by
when :months
time_suffix = "-01 00:00:00"
records = where("#{table_name}.#{date_column} > ? AND #{table_name}.#{date_column} <= ?", period_range[0].to_date, period_range[1].to_date)
"DATE_FORMAT(#{table_name}.#{date_column.to_s}, '%Y-%m')"
when :days
time_suffix = " 00:00:00"
records = where("#{table_name}.#{date_column} > ? AND #{table_name}.#{date_column} <= ?", period_range[0].to_date, period_range[1].to_date)
"DATE(#{table_name}.#{date_column.to_s})"
when :hours
time_suffix = ":00:00"
records = where("#{table_name}.#{date_column} > ? AND #{table_name}.#{date_column} <= ?", period_range[0], period_range[1])
"DATE_FORMAT(#{table_name}.#{date_column.to_s}, '%Y-%m-%d %H')"
when :minutes
time_suffix = ":00"
records = where("#{table_name}.#{date_column} > ? AND #{table_name}.#{date_column} <= ?", period_range[0], period_range[1])
"DATE_FORMAT(#{table_name}.#{date_column.to_s}, '%Y-%m-%d %H:%M')"
end
# Get counts per cycle
records = records.group(group_column).select("*, count(*) AS series_count, #{group_column} AS series_time")
series = {}
# Generate placeholder series
time_table = { :days => 60*60*24, :hours => 60*60, :minutes => 60, :seconds => 0 }
if time_by == :months
ticks = 12 * (period_range[1].year - period_range[0].year) + (period_range[1].month + 1) - period_range[0].month
else
ticks = (period_range[1] - period_range[0] + 1) / time_table[time_by]
end
ticks.to_i.times do |i|
time = period_range[1]-i.send(time_by)
time = case time_by
when :minutes
time.change(:sec => 0)
when :hours
time.change(:min => 0)
when :days
time.change(:hour => 0)
when :months
time.change(:day => 1, :hour => 0)
end
series[time.to_s(:db)] = 0
end
# Merge real counts with placeholder series
to_merge = {}
records.each do |r|
to_merge[r.series_time.to_s+time_suffix] = r.series_count
end
series.merge!(to_merge)
end
}
end
end
end
The ActiveRecord statistics gem seems like it could be really useful to you.
If the statistics gem doesn't help, the admin_data gem has some analytics built in. Check out the demo. Use of the entire admin system might be overkill but you could at least try to browse the source to mimic the analytics feature.

Rails - Building Conditions for Use in a Query

I usually like to build a conditions hash like this below:
conditions = {}
conditions[:color] = "black"
conditions[:doors] = 4
conditions[:type] = "sedan"
Cars.find(:all, :conditions=>conditions)
But how would I add a date range into this for something like:
year >= '2011-01-01' and year < '2011-02-01'
I am assuming you are using Rails 2.3.x and year is a date column.
conditions = {}
conditions[:color] = "black"
conditions[:doors] = 4
conditions[:type] = "sedan"
# create a date range
conditions[:year] = (Date.parse("2011-01-01")...Date.parse("2011-02-01"))
Car.all(:conditions => conditions)
If you want to do even more complex queries in 2.3.x use the AR Extensions gem.
Read this article for more details.
If you're on Rails 3, why not use AREL?
Cars.where(:color => "black").
where(:doors => 4).
where(:type => "sedan").
where("year >= '2011-01-01'").
where("year < '2011-02-01'")
Btw, don't use :type as a field name. Rails uses this for STI.
On Rails 2.3, I'd just build up conditions as a String instead.
You can build a up query through relations. The query will not be executed until it needs to be evaluated. This is nice for searches where some parameters are optional.
#cars = Cars.where(:color => "black")
#cars = #cars.where(:doors => 4)
#cars = #cars.where("year >= '2011-01-01'")
#cars = #cars.where("year <= '2011-02-01'")
Or you could just merge all that together into one:
Cars.where(["color=? AND doors=? AND year >= ? AND year <= ?", "black", 4, "2011-01-01", "2011-02-01"]
UPDATE:
For Rails < 3
#cars = Cars.scoped(:conditions => {:color => "black"})
#cars = #cars.scoped(:conditions => {:doors => 4})
#cars = #cars.scoped(:conditions => "year >= '2011-01-01'")
#cars = #cars.scoped(:conditions => "year <= '2011-02-01'")
OR
Cars.all(:conditions => ["color=? AND doors=? AND year >= ? AND year <= ?", "black", 4, "2011-01-01", "2011-02-01"]

Records not showing up in the database

We start an operation by making sure a customer has enough items with which to work. So we begin by collecting all their current items in an array:
#items = SOrder.where(:user_id => current_user.id).order("order")
Then we determine how many items they should have. If someone has a free account, they should have 5 items. If it is a paid account they should have 20 items:
if current_user.paid
should_have = 19 # one less than 20 because of 0 position in the array
else
should_have = 4
end
Then, in case we need to add blank records, we figure out where we should start:
if #items.empty?
start = 0
else
start = #items.length + 1
end
If the start is less than or equal to what someone should have, then we add blank records:
if start <= should_have
value = [start .. should_have].each do |v|
SOrder.create(:user_id => current_user.id, :order => v, :item_id => 0 )
end
#items = SOrder.where(:user_id => current_user.id).order("order") # reload array
end
The records that should be added are not showing up in the database.
Where is the error?
Try
value = (start .. should_have).each do |v|
instead of
value = [start .. should_have].each do |v|
[start .. should_have] will just return an array with a single range element in it. (start .. should_have) will return a range, upon which the each enumerator will work as you expect.
The error may come from calling .length from an Arel object and not a record set.
#items = SOrder.where(:user_id => current_user.id).order("order").all
However, since you only need a count for the first query, I'd suggest using .count. If I was writing this I'd do something like:
number_of_items = SOrder.where(:user_id => current_user.id).count
number_of_blank_items_to_add = current_user.allowed_items - number_of_items
if number_of_blank_items_to_add > 0
number_of_blank_items_to_add.times do |num|
SOrder.create(:user_id => current_user.id, :order => (number_of_items + num), :item_id => 0 )
end
end
#str_order = SOrder.where(:user_id => current_user.id).order("order")
In User model:
def allowed_items
if paid
20
else
5
end
end
Better Yet
In User model:
has_many :s_orders, :order => "s_orders.order asc"
def add_extra_blank_orders
number_of_items = s_orders.count
number_of_blank_items_to_add = allowed_items - number_of_items
if number_of_blank_items_to_add > 0
number_of_blank_items_to_add.times do |num|
s_orders.create(:order => (number_of_items + num), :item_id => 0 )
end
end
def allowed_items
if paid
20
else
5
end
end
In controller:
current_user.add_extra_blank_orders
#str_order = current_user.s_orders
While I am sure that you have a good reason, I am questioning why blank items need to be in the database at all. And, if a after_create hook could be used here.
Try this code to ensure your code is entering the loop of creating records by adding puts "entered the loop" inside the loop like this:
if start <= should_have
(start .. should_have).each do |v|
puts "entered loop"
SOrder.create(:user_id => current_user.id, :order => v, :item_id => 0 )
end
#items = SOrder.where(:user_id => current_user.id).order("order") # reload array
end
If "entered loop" is getting printed, try .create! to make sure all the validations are passed(If any of them are failed ActiveRecord error will be raised stating the validation)
if start <= should_have
(start .. should_have).each do |order|
SOrder.create!(:user_id => current_user.id, :order => order, :item_id => 0 )
end
#str_order = SOrder.where(:user_id => current_user.id).order("order") # reload array
end
I don't see where you're using value and not sure why you're using it.
Can you use this?:
if start <= should_have
(start .. should_have).each do |order|
SOrder.create(:user_id => current_user.id, :order => order, :item_id => 0 )
end
end
#str_order = SOrder.where(:user_id => current_user.id).order("order") # reload
Edit: I moved #str_order outside of your if statement to make sure you'd always be reloading the array, if this is undesired just switch it back.

Best way to refactor this without making as many calls as I am?

I am trying to cycle through a few of these blocks. They basically narrow down a number of people that fulfill a bunch of attributes.
I apologize if this seems really messy, but my database is really taking a toll processing this, and I know there's a better way. I'm just lost on strategy right now.
My Code:
def count_of_distribution
#beginning with an array..
array_of_users = []
# any matching zip codes? ..
# zip_codes
#zip_codes = self.distributions.map(&:zip_code).compact
unless #zip_codes.nil? || #zip_codes.empty?
#matched_zips = CardSignup.all.map(&:zip_code) & #zip_codes
#matched_zips.each do |mz|
CardSignup.find(:all, :conditions => ["zip_code = ?", mz]).each do |cs|
array_of_users << cs.id
end
end
end
# any matching interests?..
# interest
#topics = self.distributions.map(&:me_topic).compact
unless #topics.nil? || #topics.empty?
#matched_topics = MeTopic.all.map(&:name) & #topics
#matched_topics.each do |mt|
MeTopic.find(:all, :conditions => ["name = ?", mt]).each do |mt2|
mt2.users.each do |u|
array_of_users << u.card_signup.id if u.card_signup
end
end
end
end
# any matching sexes?..
# sex
#sexes = self.distributions.map(&:sex).compact
unless #sexes.nil? || #sexes.empty?
#matched_sexes = CardSignup.all.map(&:sex) & #sexes
#matched_sexes.each do |ms|
CardSignup.find(:all, :conditions => ["sex = ?", ms]).each do |cs|
array_of_users << cs.id
end
end
end
total_number = array_of_users.compact.uniq
return total_number
end
This is the most embarressing results ever :
Completed in 51801ms (View: 43903, DB: 7623) | 200 OK [http://localhost/admin/emails/3/distributions/new]
UPDATED ANSWER It is truncated but still takes a huge toll on the DB
array_of_users = []
#zip_codes = self.distributions.map(&:zip_code).compact
#sexes = self.distributions.map(&:sex).compact
#zips_and_sexes = CardSignup.find(:all, :conditions => ["gender IN (?) OR zip_code IN (?)", my_sexes, my_zips])
#zips_and_sexes.each{|cs| array_of_users << cs.id }
#topics = self.distributions.map(&:me_topic).compact
#all_topics = MeTopic.find(:all, :conditions => ["name IN (?)", #topics])
array_of_users << CardSignup.find(:all, :conditions => ["user_id IN (?)", #all_topics.map(&:users)]).map(&:id)
You are trying to let rails do all the computation through series of loops; no wonder it's taking so long.
It's hard to follow, but perhaps instead of using .each loops, try to pull out everything you are after right away, and then use a .group_by(&:attribute)
OR if your end result is just card signups.
It seems you are trying to get all the users that have something in desired, a zip, a topic, or sex. So, let the database do the work.
my_zips = #zip_codes = self.distributions.map(&:zip_code).compact.join(", ")
my_sexes = #sexes = self.distributions.map(&:sex).compact.join(", ")
all_cards = CardSignup.find(:all, :conditions => ["sex IN (?) OR zip_code IN (?)", my_sexes, my_zips])
my_topics = #topics = self.distributions.map(&:me_topic).compact.join(", ")
all_topics = MeTopic.find(:all, :conditions => ["name = ?", my_topics])
more_cards = all_topics.map{|x| x.users}.map{|n| n.card_signup}
total_number = (all_cards + more_cards).flatten.uniq
I hope this is a better answer.
Here it is. It runs super fast now :
array_of_users = []
# zips and sexes
#zip_codes = self.distributions.map(&:zip_code).compact
#sexes = self.distributions.map(&:sex).compact
#zips_and_sexes = CardSignup.find(:all, :conditions => ["gender IN (?) OR zip_code IN (?)", #sexes, #zip_codes])
#zips_and_sexes.each{|cs| array_of_users << cs.id }
# interest
#topics = self.distributions.map(&:me_topic).compact
#selected_topics = MeTopic.find(:all, :conditions => ["name in (?)", #topics]).map(&:id)
#matched_users = ActiveRecord::Base.connection.execute("SELECT * FROM `me_topics_users` WHERE (me_topic_id IN ('#{#selected_topics.join("', '")}') )")
#list_of_user_ids = []
#matched_users.each{|a| #list_of_user_ids << a[0] }
#list_of_user_ids.uniq!
array_of_users << CardSignup.find(:all, :conditions => ["user_id IN (?)", #list_of_user_ids]).map(&:id)
# age
#ages = self.distributions.map(&:age).compact
#ages_array = []
#ages.each{|a| #ages_array << how_old(a) }
#ages_array.each{|aa| array_of_users << aa.id}
array_of_users << CardSignup.all.map(&:id) if array_of_users.flatten.empty?
total_number = array_of_users.flatten.uniq
return total_number

Resources