comparison of Symbol with 100 failed - ruby-on-rails

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.

Related

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")

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)

Pluck and ids give array of non unique elements

In console:
Course.ids.count
=> 1766
Course.pluck(:id).count
=> 1766
Course.ids.uniq.count
=> 1529
Course.count
=> 1529
It's normal?
small comment - model Course uses ancestry (gem).
UPD1:
Generated sql:
Learn::Course.ids.count
(5.4ms) SELECT "learn_courses"."id" FROM "learn_courses" LEFT OUTER JOIN "learn_course_translations" ON "learn_course_translations"."learn_course_id" = "learn_courses"."id"
=> 1766
Learn::Course.count
(1.5ms) SELECT COUNT(*) FROM "learn_courses"
=> 1529
hmm...
UPD2:
Schema Information
#
# Table name: learn_courses
#
# id :integer not null, primary key
# name :string(255)
# position :integer
# created_at :datetime
# updated_at :datetime
# ancestry :string(255)
# course_type :string(255)
# article :string(255)
# item_style :integer
# hidden :boolean
# score :integer default(0)
# next_id :integer
# first :boolean
You should be able to work around this with
Learn::Course.pluck('distinct learn_courses.id')
The problem is that LEFT OUTER JOIN with learn_course_translations, which must have multiple rows per Learn::Course, resulting in the same learn_courses.id appearing several times. pluck doesn't care about distinctness, so it just passes them all back.
Maybe ancestry adds default_scope to your model. Try to check it with
Learn::Course.unscoped.ids.count

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

Dealing with time periods in Rails 3.1

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)

Resources