Check if a datetime field occurs within a time period - ruby-on-rails

I'm trying to check if an assignment's due_date (datetime data type) occurs within the start and end of the current week (and later on within the next 2 weeks). The logic I need help with is the 'due_date: Date.today.beginning_of_week..Date.today.end_of_week' section. If you could help me write those two queries, that would be awesome. Thanks in advance.
#student_assignments = StudentAssignment.joins(:assignment).where("DATE(assignments.due_date) = ?", Date.today.beginning_of_week..Date.today.end_of_week).all

You are very close. One way to achieve it is:
#student_assignments = StudentAssignment.joins(:assignment).where("Date(assignments.due_date) BETWEEN ? AND ?", Date.today.beginning_of_week, Date.today.end_of_week)

Related

Get first entry from an associated table Ruby on Rails

I have a one to many relationship: User has many Payments. I am trying to find a query that gets the first payment of each user(using created_at from the payments table).
I have found a similar question with an SQL response, but I have no idea how to write it with Active Record.
how do I query sql for a latest record date for each user
Quoting the answer:
select t.username, t.date, t.value
from MyTable t
inner join (
select username, max(date) as MaxDate
from MyTable
group by username
) tm on t.username = tm.username and t.date = tm.MaxDate
For me, it would be min instead of max.
Thank you :)
Try this one for POSTGRES
Payment.select("DISTINCT ON(user_id) *").order("user_id, created_at ASC")
And For SQL
Payment.group(:user_id).having('created_at = MAX(created_at)')
If I'm going to answer the question above with: (I don't based on given raw SQL)
User has many Payments. I am trying to find a query that gets the first payment of each user(using created_at from the payments table).
Let say:
# Assumed to have a Single User, as reference
user = User.first
# Now, get first payment (from Payment model)
user.payments.last
# .last since it will always get the first created row by created_at.
If I fully understand what you're trying to do. I'm don't know why you need max or min date?
What about this?
If you want first payment of each user
dates = Payment.group(:user_id).minimum(:created_at).values
payments = Payment.where(created_at: dates)
From payment you can find user too.
I think you have username as foreign key, you can change accordingly. :)
Let me know if you face any issue, as I tested it works.
I know this answer is not the best, but it will work even or transactions with milliseconds difference, as rails saves date(created_at and updated_at) with ms level.
I am sorry for not replying to everything, but after multiple tests, this is the quickest answer (in run time) I came with:
Payment.where(:id => Payment.group(:user_id).pluck(:id))
I am saying it might not be the quickest way because I am using a sub query. I am getting the unique values and getting the ID's:
Payment.group(:user_id).pluck(:id)
Then I am matching those ID's.
The downside of this is that it won't work reversed, for getting the last payment.
There was also a possibility to use group_by and map but, since map is coming from ruby, it is taking much more time.
I'm not sure but try this :
In your controller :
def Page
#payments = Payment.first
end
in your html.erb :
<% #payments.each do |payment| %>
<p> <%= payment.amount %> </p>
Hope this help !
Record.association.order(:created_at).first

Rails Active Record .where statement optimization

I'm building an ecommerce site and want to use an active record where statement to find shipments that are scoped to a certain supplier and certain shipment states. Here's what I have now:
Spree::Shipment.where("stock_location_id = ? and "state = ?", spree_current_user.supplier.stock_locations.first.stock_location_id, 'shipped' || 'ready')
I've found that this results in only 'shipped' statements get returned. I'd like it to display both shipped, and ready shipments. So far I can only get it to show one or the other, depending on if i put 'shipped' or 'ready' first in the query.
I'm guessing I have put the OR operator (||) in the wrong place, even though there are no errors. Can someone tell me a proper way to place OR operators in a condition in the where statement?
Thanks,
Brandon
id = spree_current_user.supplier.stock_locations
.first.stock_location_id
Spree::Shipment.where(stock_location_id: id, state: %w[shipped ready])
I figured out my answer as I was writing out the question a bit more. I wanted to share the answer in case anyone else comes across this. This seemed to do the trick:
Spree::Shipment.where("stock_location_id = ? and (state = ? or state = ?)", spree_current_user.supplier.stock_locations.first.id, 'shipped', 'ready')
I tested it in the console and it returned this SQL Output with shipments in both 'ready' and 'shipped' states:
Spree::Shipment Load (0.6ms) SELECT "spree_shipments".* FROM "spree_shipments" WHERE (stock_location_id = 8 and (state = 'shipped' or state = 'ready')) LIMIT 15 OFFSET 0
Hope this can help others. Also, if you notice that this statement seems weird or inefficient I would really like to know.
Thanks!

Rails - create or update according to time range

I have a table where I need to create a new entry or update if one of the columns is inside a range of dates (today).
For example, I have a table of shuttles registration with [name, time] where name represents someone and time is when he would like to take the shuttle on.
Each (name) can register at most once a day.
When someone registers, I would like to update an existing row (of the same day), if exists, or create a new one.
The following query extracts the relevant row, if exists:
Shuttle.where('name= ? AND time BETWEEN ? AND ?', params[:name], DateTime.now.beginning_of_day, DateTime.now.end_of_day)
Tried to use first_or_create and equivalents but couldn't find the right syntax to apply the range query.
Any idea?
Thanks
Try this:
Shuttle.where(:name => params[:name]).
where(:time => DateTime.now.beginning_of_day..DateTime.now.end_of_day)).
first_or_create(:time => DateTime.now)
Reference: http://guides.rubyonrails.org/v3.2.13/active_record_querying.html#first_or_create
first_or_create doesn't apply in this scenario, because the condition doesn't make sense as the value of an update. It would be like saying "Find a shuttle where the time is any time on this given day, and if you don't find one then create one where the time is any time on this given day". That doesn't make sense: you need a specific time when you create a shuttle.
You need to think about the logic of what you want to do: it's not obvious to me at least.
If I understand you, I would do the following
#shuttle = Shuttle.where('name= ? AND time BETWEEN ? AND ?', params[:name], DateTime.now.beginning_of_day, DateTime.now.end_of_day).first
#shuttle.columnname = "new value"
#shuttle.save
If you are doing multiple
#shuttles = Shuttle.where('name= ? AND time BETWEEN ? AND ?', params[:name], DateTime.now.beginning_of_day, DateTime.now.end_of_day)
#shuttles.each do |shuttle|
shuttle.columnname = "new value"
shuttle.save
end
What about something like this?
#shuttle = Shuttle.where(
name: params[:name],
time: DateTime.now.beginning_of_day..DateTime.now.end_of_day
).first_or_initialize do |s|
s.attribute = value
s.save
end

Rails & Sqlite question - comparing dates to db timestamps

I'm trying to do this: map the total sales on a day to an array of dates for highcharts (yes my project is effectively exactly the same as the railscast example).
I'm unfortunately just ending up with a lot of 0s; I believe the piece in my model:
def self.total_revenue_on(date)
where("date(created_at) = ?", date).sum(:amt)
end
is failing to match the date to the datetime written in my database, e.g. "2011-07-21 09:22:28.388944+0000". Pretty sure that's where it's failing because if I remove the timezone piece manually from my database (get rid of "+0000" and leave just "2011-07-21 09:22:28.388944") it works just fine.
I think this is really a rails/sqlite question: am I storing the timestamp improperly, or comparing improperly? Any help is greatly appreciated!
The best practice is to use to_s(:db) for referencing datetimes in a database in Rails. Try:
def self.total_revenue_on(date)
where("date(created_at) = ?", date.to_s(:db)).sum(:amt)
end
OK, I managed to solve this by using a different lookup method:
def self.total_revenue_on(date)
where("datetime >= ? and datetime < ?", date, date + 1.day).sum(:amt)
end
Still completely perplexed by the problem with the original, but this seems to be working.

How do I write an activerecord query that only looks at the time component of a datetime field?

Is it possible to do an activerecord query that only looks at the time component of a datetime field?
e.g. Battle.where('start_time < ? and start_time > ?','12:00','06:00')
to find all battles that were started between 6am and 12pm regardless of the day they occurred? In this example, start_time is defined as a datetime.
The only way to do this is using a SQL function, if you're on MySQL you could do it like this:
Battle.where( 'HOUR( start_time ) >= ? AND HOUR( start_time ) <= ?', 12, 6 )
But this is hugely inefficient and is always going to generate a full table scan and you surely don't want that.
The best solution is to add columns with the hour values alone at your battle model, index them and query directly on them like this:
Battle.where( 'start_time_hour >= ? start_time_hour <= ?', 12, 6 )
Just add an before_save callback that sets this values before saving your Battle model based on the start_time property.
EDIT
BTW, if you're using PostgreSQL, it's capable of creating an index on the result of a function, so, if you don't mind having to stick with PostgreSQL you could just use the SQL function. And before you ask, no, i don't know how it works, I only know the database allows you to do so and that this is not available on MySQL.

Resources