Rails collection of current + past 30 days - ruby-on-rails

So I've got a collection called #events and I am defining it as
#organization.owned_events.current.reverse
However, what I would actually like to do is have not actually current, but anything current or expired in the last 30 days. I tried
#organization.owned_events.current-30.days.reverse
But this just provided an empty collection. I think this is because what I actually want is not current-30.days, but actually current AND current-30.days
Not sure how to write that though. Can I somehow define another term instead of current like net_thirty and make that equal current or within last 30 days?

In your event model you can do:
scope :from, ->(duration){ where('your_datetime_field_here > ?', Time.zone.now - duration ) }
Since you want to filter by a date or datetime field, previous option is for a datetime field, if you want to filter by a date field:
scope :from, ->(duration){ where('your_date_field_here > ?', Time.zone.today - duration ) }
->(){} is a block you are passing to the scope.
You would use these scopes like this:
any_events.from(30.days)
In your case:
#organization.owned_events.from(30.days)
You could also want to filter events not after today:
scope :from, ->(duration){ where('your_date_field_here BETWEEN ? AND ? ', Time.zone.today - duration, Time.zone.today ) }

Related

Find model records against Time.now

I have a model with 2 specific fields named start and end.
Those are dates I want to check against current date ( Time.now ) in order to get only records where current time is comprised between start and end stored values
I tried a few things such as
Model.all.find_by(start: <Time.now , end: >Time.now)
But I get syntax errors all the time. Sometimes the field name of the second date ( end ) is even mixed up with the 'end' keyword inside my controller.
You can try something like this:
Model.where('start < ? and end > ?', Time.now, Time.now)
By the way, all is redundant in this situation.

Rails named scope for left joined model

I have a table named acts, and I'll like to run a query that rolls up act values for a whole week. I'd like to make sure the query always returns one row for each day of the week, even if there are no records for that day. Right now I'm doing it like this:
def self.this_week_totals
sunday = Time.now.beginning_of_week(:sunday).strftime("%Y-%m-%d")
connection.select_values(<<-EOQ)
SELECT COALESCE(SUM(end_time - start_time), '0:00:00') AS total_time
FROM generate_series(0, 6) AS s(t)
LEFT JOIN acts
ON acts.day = '#{sunday}'::date + s.t
WHERE deleted_at IS NULL
GROUP BY s.t
ORDER BY s.t
EOQ
end
Is there any way I could make this a named scope on the Act class so it can be combined with other conditions, for example to filter the Acts by a client_id? Since acts isn't in my FROM, but is part of the LEFT JOIN, I'm guessing not, but perhaps someone out there knows a way.
Edit: Just to clarify, the goal is for this method to always return exactly 7 Act objects, regardless of what's in the database.
if you want your query object to be chainable it must be an ActiveRelation object
where, select, order and the other Arel objects return ActiveRelation objects that are chainable, so if the below works you can chain off of the returned query object
note in rails 3 and up having a class method that returns an ActiveRelation is basically the same as a scope, they are both chainable query objects
class Act
def self.this_week_totals
sunday = Time.now.beginning_of_week(:sunday).strftime("%Y-%m-%d")
select("COALESCE(SUM(end_time - start_time), '0:00:00') AS total_time")
.from("generate_series(0, 6) AS s(t)")
.joins("LEFT JOIN acts ON acts.day = '#{sunday}'::date + s.t")
.where("deleted_at IS NULL")
.group("s.t")
.order("s.t")
end
# ...
end
client_id = params[:client_id]
Act.this_week_totals.where("client_id = ?", client_id)
http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-from
Although I really thought I could use the solution from #house9, I don't see any way to avoid compromising on at least one of these goals:
Always yield 7 Act objects.
Return an ActiveRelation so I can compose this method with other scopes.
Permit joining to the clients table.
Here is the part-SQL/part-Ruby solution I'm actually using, which sadly gives up on point #2 above and also returns tuples rather than Acts:
def self.this_week(wk=0)
sunday = Time.now.beginning_of_week(:sunday)
sunday += wk.weeks
not_deleted.where("day BETWEEN ? AND ?", sunday, sunday + 7.days)
end
scope :select_sum_total_hours,
select("EXTRACT(EPOCH FROM COALESCE(SUM(end_time - start_time), '0:00:00'))/3600 AS total_hours")
scope :select_sum_total_fees,
joins(:client).
select("SUM(COALESCE(clients.rate, 0) * EXTRACT(EPOCH FROM COALESCE(end_time - start_time, '0:00:00'))/3600) AS total_fees")
def self.this_week_totals_by_day(wk=0)
totals = Hash[
this_week(wk)
.select("EXTRACT(DAY FROM day) AS just_day")
.select_sum_total_hours
.select_sum_total_fees
.group("day")
.order("day")
.map{|act| [act.just_day, [act.total_hours.to_f, act.total_fees.to_money]]}
]
sunday = Time.now.beginning_of_week(:sunday)
sunday += wk.weeks
(0..6).map do |x|
totals[(sunday + x.days).strftime("%d")] || [0, 0.to_money]
end
end
That could be DRYed up a bit, and it would produce errors if there were ever a month with fewer than 7 days, but hopefully it shows what I'm doing. The scopes for this_week, select_sum_total_hours, and select_sum_total_fees are used elsewhere, so I want to pull them out into scopes rather than repeating them in several big raw SQL strings.

Rails - How to check what the record count of a model 24 hours ago

I want to display the rate of change in the total number of records for a certain model. So I need to know, at any given time, how many there were 24 hours ago, and how many there are now.
It is very likely that a record will be deleted by users, so I can't just use the value of existing records that are older than 24 hours.
Is there a simple way to do this?
Thanks
class Item < ActiveRecord::Base
scope :since, lambda {|time| where("created_at > ?", time) }
scope :during_last, lambda {|time| where("created_at > ?", (Time.now - time)) }
end
Item.since(Time.now - 24.hours).count
Item.since(24.hours.ago).count
Item.during_last(24.hours).count
Well, you could do a count against the column created_at.
def count_rate(model, time=DateTime.now)
current_count = model.count(:conditions => ["created_at < ?", time])
last_24_hours_count = model.count(:conditions => ["created_at < ?", time-24.hours])
current_count - last_24_hours_count
end
This method count the record at a given compared with the last 24 hours.
Well, in case the user has deleted those records back, you would should create another table Rate(date, hour, count) and record every hour by using observer (only after_create).
def after_create
rate = Rate.find_or_initialize_by(Date.today.to_s, Time.now.hour)
rate.increment(:count)
rate.save!
end
In this observer, if it finds the existing record by date and hour, it simply increment the count column. You add the default value to zero on count column as well. Later on, you can query the data from that table by date and hour.

rails date ranges for same date

Query below works fine when user selects different dates but when user selects same dates like 3/5/2011 and 3/5/2011 it returns nothings. How can i handle this ? If user selects same dates, i want it to find clients which are created at that date.
Client.where(:created_at => date_from..date_to)
You may need to modify your query as below to get between beginning_of_day and end_of_day
Client.where(:created_at => date_from.beginning_of_day..date_to.end_of_day)
You could create a helper method:
def date_range(from, to)
from == to ? from : from..to
end
Client.where(:created_at=>date_range(date_from,date_to))

Rails: default scoping being cached by query cache?

I got a default scoping like this which is dynamic:
default_scope :conditions => ["departure_date >= ?", DateTime.current.beginning_of_day]
When I use this code the first day is ok. Lets say first day is 28-03-2011
But the next day seems like it's still using "departure_date >= 28-03-2011"
Is my default scoping being cached?
The problem is that that code is only being executed once, when your app is loaded, and thus the actual date isn't changing. You need to change it to load lazily:
default_scope lambda { { :conditions => ["departure_date >= ?", DateTime.current.beginning_of_day] } }
This way, Datetime.current.beginning_of_day will be evaluated each time you make a query.

Resources