Dealing with time periods in Rails 3.1 - ruby-on-rails

I'm developing a booking system in Rails 3.1. I have created a model for a Booking:
# == Schema Information
#
# Table name: bookings
#
# id :integer not null, primary key
# product_id :integer
# customer_id :integer
# booked_from :datetime
# booked_to :datetime
# paid :boolean
# payment_type :string(255)
# created_at :datetime
# updated_at :datetime
#
So what I want to do is to validate each entry and check whether the desired time period (booked_from - booked_to) is overlapping any period of another booking with the same product_id. The products also have an available_from and available_to field which it also has to validate against.
How do I do this?

Check if this works:
class Booking
validate :booking_period_not_overlapped
private
def booking_period_not_overlapped
unless Booking.where(
'(booked_from <= ? AND booked_to >= ?) OR (booked_from >= ? AND booked_from <= ?)',
booked_from, booked_from,
booked_from, booked_to
).empty?
errors.add(:booked_from, 'Invalid period.')
end
end
end
It just checks if there is any existing records whose booked_from and booked_to satisfy one of the following conditions (suppose your new booking is from 16:00 to 17:00):
it starts before the new booking, and not yet ended (e.g. 15:00 - 16:30 or 15:00 - 17:30)
it starts within the new booking period (e.g. 16:20 - 16:50 or 16:30 - 17:30)

Related

comparison of Symbol with 100 failed

I am re-writing a program called Emissions Gateway to the new version of Ruby on Rails.
I have a method that was written with syntax from a gem called Squeel and I am having a very hard time re-writing it. I have been failing at it for over 4 hours and can't seem to get it figured out.
This is the method right here, it is in a model called datalogger.rb along with the schema information for the datalogger.rb model.
# == Schema Information
#
# Table name: dataloggers
#
# id :integer not null, primary key
# project_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# original_file_name :string(255)
# original_content_type :string(255)
# original_file_size :integer
# original_updated_at :datetime
# status :string(255) default("incomplete")
# hours :integer
# readings_total :integer
# readings_start :datetime
# readings_stop :datetime
# direct_upload_url :string(255)
# version :string(255)
# comments :string(255)
# bypass :boolean default(FALSE)
# device_id :integer
# device_name :string(255)
# device_description :string(255)
# device_serial :integer
# md5 :string(255)
# user_id :integer
# reported_at :datetime
# user_reported_id :integer
# reported :boolean
def stats(temperatures)
unless bypass
#temperatures = temperatures
#stats = {}
#cumulative_percent_at = 0
#cumulative_readings = 0
#temperatures.each_cons(2) do |current_n, next_n|
# puts "Evaluating #{current_n} and #{next_n}"
#stats["#{next_n}"] = {}
# CHANGED v0.009 9/27/2021 Scott Milella
# readings_at = readings.where{(temperature.gt current_n) & (temperature.lteq next_n)}.sum(:frequency)
readings_at = Reading.where(:temperature > current_n).and(:temperature <= next_n).sum(:frequency)
#stats["#{next_n}"][:readings] = readings_at
# puts "Readings at: #{readings_at}"
# #cumulative_readings = #stats.map{|_, v| v[:readings] }.sum
# puts "Cumulative Readings: #{cumulative_readings}"
percent_at = ((readings_at.to_f / readings_total) * 100 )
#stats["#{next_n}"][:time_at] = percent_at
#cumulative_percent_at += percent_at
# puts "Percent at: #{percent_at}%"
# puts "Cumulative Percent at: #{#cumulative_percent_at}"
percent_over = 100 - #cumulative_percent_at
#stats["#{next_n}"][:over] = percent_over
# puts "Percent Over: #{percent_over}%"
# puts "Progress: #{#cumulative_readings}/#{readings_total} readings"
end
end
This is the method I changed:
readings_at = Reading.where(:temperature > current_n)
.and(:temperature <= next_n).sum(:frequency)
You can see what I changed above in the method as well as I indicate it with # CHANGED. It is giving me this error called comparison of Symbol with 100 failed which makes NO Sense to me because the :symbol is an integer from another model called Reading.
Here is that model:
# == Schema Information
#
# Table name: readings
#
# id :integer not null, primary key
# temperature :integer
# frequency :integer
# datalogger_id :integer
#
class Reading < ActiveRecord::Base
belongs_to :datalogger
attr_accessible :frequency, :temperature, :datalogger_id
validates_presence_of :frequency, :temperature, :datalogger_id
end
I don't understand why I can't compare an Integer with an Integer regardless if it is in a symbol or not? Do I have the Syntax wrong or something? It isn't giving me a syntax error. I have tried about 1000 other ways to write it and I get a variety of errors from > not found in Array to all kinds of other things. If anyone wants to see the whole datalogger.rb model I will post it, it's rather long and it seems to be just this method that the problem exists in.
Here is a single line I have captured from the SQL out of the current version of Emissions Gateway that is working: You can see the number 272 should be current_n and the 150 is the next_n I can verify those values on the better_errors console. So I don't understand where I am going wrong. I am guessing it might have something to do with the each_cons method perhaps which I do not understand.
I modified it so you could see the SQL all in one place, otherwise it was displaying as one long line. I will show it after just in case it is confusing:
2021-09-27T18:50:49.173173+00:00 app[web.1]: (1.5ms) SELECT SUM("readings"."frequency")
AS sum_id FROM "readings"
WHERE "readings"."datalogger_id" = 272
AND (("readings"."temperature" > 100
AND "readings"."temperature" <= 150))
The SQL as it comes out
2021-09-27T18:50:49.173173+00:00 app[web.1]: (1.5ms) SELECT SUM("readings"."frequency") AS sum_id FROM "readings" WHERE "readings"."datalogger_id" = 272 AND (("readings"."temperature" > 100 AND "readings"."temperature" <= 150))
If anyone can point out how I need to re-write this method I would be greatly appreciative, I have tried for hours and am getting noplace.
Here is the instructions for squeel in case anyone needs to see the instructions.
https://github.com/activerecord-hackery/squeel
I wish that gem had NEVER been written, has caused me so much pain it is unreal!
Thank You,
Scott
Ok, let's take a deep look into your query:
readings_at = Reading.where(:temperature > current_n).and(:temperature <= next_n).sum(:frequency)
Both :temperature > current_n and :temperature <= next_m are comparing symbols (left side) with integers (right side). That's why you are getting an ArgumentError.
The Rails syntax to achieve what you are doing is:
readings_at = Reading.where('temperature > ? AND temperature <= ?', current_n, next_n).sum(:frequency)
Or, if you prefer, adding multiples where will add an AND clause to your query. So the below is equivalent:
readings_at = Reading.where('temperature > ?', current_n).where('temperature <= ?', next_n).sum(:frequency)
Using the ? guarantee that Rails is going to "clean" this input for you in order to prevent SQL injection.

Multiple group queries using sum & count

So for the given model
# == Schema Information
#
# Table name: faction_armories
#
# id :bigint not null, primary key
# action :string
# qty :integer
# tid :string
# timestamp :datetime
# user_name :string
# created_at :datetime not null
# updated_at :datetime not null
# faction_id :integer
# item_id :integer
# user_id :integer
#
I'm attempting to group user_name, action, and item_id, and add up all the qty's from each entry. For example
action="used","user_name"="me","qty"=5,"item_id"=4
action="used","user_name"="me","qty"=10,"item_id"=4
Should return a hash that identifies that me - used item 4 - 15 times.
Currently, I'm running two queries but still, the data is not coming through correctly, any input would be greatly appreciated
#news = FactionArmory.where(faction_id: current_user.faction_id).order(user_name: :asc).where("timestamp >= ?", params["report_start"]).where("timestamp <= ?", params["report_end"]).includes([:item]).group("user_name", "action", "item_id").count
#qty = FactionArmory.where(faction_id: current_user.faction_id).order(user_name: :asc).where("timestamp >= ?", params["report_start"]).where("timestamp <= ?", params["report_end"]).includes([:item]).group("user_name", "action", "item_id").sum(:qty)
You can select or pluck to get multiple calculations in one query
FactionArmory
.group("user_name", "action", "item_id")
.pluck("user_name", "action", "item_id", "count(distinct faction_armories.id) as count", "sum(qty) as qty_sum")

Arel::SelectManager how to access the result including the projections

I am attempting to access the results of a join where columns of both tables are specified as part of a projection.
I have 2 models (Rails 4.2.11; Arel 6.0.4; Ruby 2.5.3)
# Table name: users
#
# id :integer not null, primary key
# name :string(255)
# email :string(255)
# created_at :datetime not null
# updated_at :datetime not null
class User < ActiveRecord::Base
has_many :photos
end
# Table name: photos
#
# id :integer not null, primary key
# name :string(255)
# created_by_id :integer
# assigned_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# user_id :integer
class Photo < ActiveRecord::Base
belongs_to :user
end
creators = User.arel_table.alias('creators')
updaters = User.arel_table.alias('updaters')
photos = Photo.arel_table
photos_with_credits = photos.
join(photos.join(creators, Arel::Nodes::OuterJoin).on(photos[:created_by_id].eq(creators[:id]))).
join(photos.join(updaters, Arel::Nodes::OuterJoin).on(photos[:assigned_id].eq(updaters[:id]))).
project(photos[:name], photos[:created_at], creators[:name].as('creator'), updaters[:name].as('editor'))
# generated SQL
SELECT photos.name, photos.created_at, creators.name AS creator, updaters.name AS editor
FROM photos
INNER JOIN (SELECT FROM photos LEFT OUTER JOIN users creators ON photos.created_by_id = creators.id)
INNER JOIN (SELECT FROM photos LEFT OUTER JOIN users updaters ON photos.updated_by_id = updaters.id)
How I'd like to process the result
photos_with_credits.map{|x| "#{photo.name} - copyright #{photo.created_at.year} #{photo.creator}, edited by #{photo.editor}"}.join('; ')
This may be a very dumb question, but ...
I have not been able to find a way to use the SelectManager result to produce a meaningful output since map was deprecated (& removed) from the SelectManager class.
I would appreciate your help.
Simplified the activerecord / arel query builder
amended code
photos_with_credits = Photo.select([photos[:name], photos[:created_at], creators[:name].as('creator'), updaters[:name].as('editor')]).
joins(photos.outer_join(creators).on(photos[:created_by_id].eq(creators[:id])).join_sources).
joins(photos.outer_join(updaters).on(photos[:assigned_id].eq(updaters[:id])).join_sources)
photos_with_credits.map do |photo|
puts "#{photo.name} - copyright #{photo.created_at.year} #{photo.creator}, edited by #{photo.editor}".light_blue
end
amended SQL (simpler)
SELECT photos.name, photos.created_at, creators.name AS creator, updaters.name AS editor
FROM photos
LEFT OUTER JOIN users creators ON photos.created_by_id = creators.id
LEFT OUTER JOIN users updaters ON photos.assigned_id = updaters.id
We should figure out a way to trigger SelectManage and get ActiveRelation result, then we can iterate it by using map, etc.
So I would like to suggest:
Photo.join(photos_with_credits.join_sources).map do |photo|
"#{photo.name} - copyright #{photo.created_at.year} #{photo.creator}, edited by #{photo.editor}"}.join('; ')
end

Getting most liked news from the query

There are two models:
# == Schema Information
#
# Table name: news
#
# id :integer not null, primary key
# title :string not null
# content :text not null
# scope :string not null
# created_at :datetime not null
# updated_at :datetime not null
# person_id :integer not null
# == Schema Information
#
# Table name: likes
#
# id :integer not null, primary key
# like :boolean
# person_id :integer not null
# news_id :integer not null
Relation
news has many likes
like belongs to news
I want to get most liked news from query. Query should substract count of likes equal true from likes equal false. The highest number is most liked news.
What I tried:
#count_true_likes = Like.where('likes.like = ?', true).group(:news_id).count
#count_false_likes = Like.where('likes.like = ?', false).group(:news_id).count
Result is Hash with id and counted likes. I don't have idea how to subtract in query positive likes from negative likes, and do it for every news.
This kind of querying becomes prohibitively slow very quickly, as your dataset grows. A common workaround is to cache number of upvotes and downvotes. For example
# Table name: news
#
# id :integer not null, primary key
# title :string not null
# content :text not null
# scope :string not null
# created_at :datetime not null
# updated_at :datetime not null
# person_id :integer not null
#
# upvotes_count :integer not null
# downvotes_count :integer not null
# vote_result :integer not null
Where vote_result is a cached upvotes_count - downvotes_count.
Then simply do
News.order(vote_result: :desc).limit(10) # top 10 articles
The downside is, of course, that you need to maintain those cached counters (increase/decrease corresponding ones when you register a vote).
I resolved my problem:
#most_liked_news_id = Like.group(:news_id)
.select('news_id, SUM(case when likes.like then 1 else -1 end) as max_positive')
.order('max_positive desc').map(&:news_id).first
#most_liked_news = News.find(#most_liked_news_id)

Rails filter on date returning no results

I'm trying to filter records that were assigned on particular day (today) using this query:
assignments = p.assignments.where("assigned_date = ?", Date.today)
Even though I know these records exist, I always get a blank result back.
I've also tried ...where(:assigned_date => Date.today) with no luck.
The database schema is:
# == Schema Information
#
# Table name: assignments
#
# id :integer not null, primary key
# name :string(255)
# rep :integer
# set :integer
# instructions :text
# created_at :datetime not null
# updated_at :datetime not null
# player_id :integer
# actual_percentage :float
# predicted_percentage :float
# assigned_date :date
#
And in the console, when I type
p.assignments.first.assigned_date == Date.today
it returns true.
Any idea what I'm doing wrong?
DateTime holds a date and a time, so you're looking for records that have a precise value, not just the same day.
assignments = p.assignments.where('assigned_date BETWEEN ? AND ?', DateTime.now.beginning_of_day, DateTime.now.end_of_day).all
should return what's expected
P.S.
all credits to Tadman

Resources