In my Rails application I have a User model ,Department model, Group model and a Register model.User model has basic user information,
User Model:
id , name
has_and_belongs_to_many :departments , :groups
Department Model:
id,name
has_and_belongs_to_many :users
has_many :registers
Group Model:
id,name
has_and_belongs_to_many :users
has_many :registers
Register Model:
date ,department_id , group_id , one , two , three
belongs_to :department ,:group
Among the Register Model "one" , "two" , "three" are time slots say: 9-12 , 12-3 , 3-6.Every day each user has to mark attendance so that their attendance has to mark against the time slot in Register table. How to mark attendance based on current time with the time slot.
Thanks in advance.
You may need to do
current_time = Time.now
if (9..11).include?(current_time.hour)
# Do A
elsif (12..14).include?(current_time.hour)
# Do B
elsif (15..18).include?(current_time.hour)
# Do C
end
(or)
current_time = Time.now
if (9...12).include?(current_time.hour)
# Do A
elsif (12...15).include?(current_time.hour)
# Do B
elsif (15...18).include?(current_time.hour)
# Do C
end
# I think of the extra "." as 'eating' the last value so you don't have it.
to deal with the overlaps
try this
current_time = Time.now
if (9..12).include?(current_time.hour)
# 9-12
elsif (12..15).include?(current_time.hour)
# 12-3
elsif (15..18).include?(current_time.hour)
# 3-6
end
Might consider creating a bitmap. A 32 bit integer is lightweight and gives you 0-24, one for each hour. Write some functions that can compare, set,... hour ranges.
Related
I have 3 classes defined as:
Client
class Client < ActiveRecord::Base
belongs_to :provider
has_many :meeting_times
end
Provider
class Provider < ActiveRecord::Base
has_many :clients
has_many :meeting_times
end
MeetingTime
class MeetingTime < ActiveRecord::Base
belongs_to :meetingable, polymorphic: true
end
Each instance of MeetingTime has a day, start_time and end_time.
Is there a way for me to efficiently compute the intersection of
client_instance1.meeting_times and provider_instance1.meeting_times
In my seed file, I create meeting_times for clients and providers as follows
client_instance1.meeting_times.build(:day => "Friday", :start_time => time1, :end_time => time2)
client_instance1.meeting_times.build(:day => "Saturday", :start_time => time3, :end_time => time4)
client_instance1.save!
Currently when I try
client_instance1.meeting_times & provider_instance1.meeting_times
after developing the client and provider instances in a seed file, I get an empty set because the ids of different meeting times don't match (even if the day, start_time and end_time are the same).
How could I resolve this issue? Am I misunderstanding something?
I believe you can do:
intersections = client_instance1.meeting_times.reduce([]) do |result, client_meeting|
intersects = provider_instance1.meeting_times.any? { |provider_meeting| provider_meeting.day == client_meeting.day && provider_meeting.start_time == client_meeting.start_time && provider_meeting.end_time == client_meeting.end_time }
result << obj if intersects
result
end
But following this approach, keep in mind, it'll return only client_instance1 meetings, which have equivalent date/time in provider_instance1 meetings.
The smarter approach - is to build a hash, where the keys will be a checksum of string like: "#{day}#{start_time}#{end_time}".
And values - are arrays of equal meetings, both from client and providers meetings.
User.rb
has_many :votes
Event.rb
has_many :votes
Vote.rb
belongs_to :user
belongs_to :event
I am trying to render all the Events that the current_user has not voted on.
Consider this situation:
There are 100 events Event.includes(:votes).count ==> 100
The User has voted on 5 events current_user.votes.count ==> 5
There are 75 events with at least one vote from other users
20 of the events have not received votes from any users
The result I'm looking for should render the 95 events that have not been voted on by the current_user, including events that have not been voted on by any user.
This query gets all the events that NO users have voted on:
Event.includes(:votes).where(:votes => {:id => nil}).references(:votes).count ==> 20
What query can I use to get all the events that have been voted on by users excluding those that have been voted on by current user (should return 75 in the example above)? I tried the below query but it returned 80 (it included events voted on by the current_user):
Event.includes(:votes).where.not(:votes => {:id => current_user}).references(:votes).count ==> 80
What query can I use to get events with votes, excluding ones that current user voted for
Can I combine the two queries into one?
Hacky but fun answer
class Event
has_many :votes
has_many :user_votes,
->() { where({user_id: Thread.current[:user]&.id}.compact) },
class_name: 'Vote'
def self.using_user(user)
old_user, Thread.current[:user] = Thread.current[:user], user
yeild
ensure
Thread.current[:user] = old_user
end
end
Event.using_user(current_user) do
Event.includes(:user_votes).where("votes.id IS NULL")
end
Original answer:
Event.
joins(
"LEFT OUTER JOIN votes ON votes.event_id = events.id AND " +
"votes.user_id = #{user.id}"
).
where("votes.id IS NULL")
Can I combine the two queries into one?
Use merge if you want to combine using AND:
results = frecords.merge(lrecords)
If you want to combine using OR, use or (only available in ActiveRecord 5+):
results = frecords.or(lrecords)
What query can I use to get events with votes, excluding ones that current user voted for.
events_current_user_voted_on = current_user.votes.pluck(:event_id)
events_all_users_voted_on = Vote.pluck(:event_id).uniq
events_only_others_voted_on = events_all_users_voted_on - events_current_user_voted_on
# 75 events with at least one vote from other users
Event.where(id: events_only_others_voted_on)
Answer for your first question:
I have modified your tried query a little bit so try this
Event.includes(:votes).where.not(:votes => {:user_id => current_user}).references(:votes).count
notice the where condition. You need to check user_id column of votes
or you can try string syntax like:
Event.includes(:votes).where.not("votes.user_id" => current_user).references(:votes).count
about your second question, I am not sure I understand your requirement. If you need to filter events with at least 1 vote and from other user only then just add another to where statement .
Event.includes(:votes).where.not("votes.user_id" => current_user, "votes.user_id" => nil).references(:votes).count
And BTW, I think you have a error in vote.rb you have specified a association to itself instead of association to Event i.e belongs_to :event
Let's say I have a User model. User has 2 has_many associations, that is User has many pencils and has many cars. Cars and Pencils table has same attribute, :date, and separate such as :speed(car) and :length(pencil). I want to join a user's pencils and cars on their common attribute, :date, so that I have an array/relation [:date, :speed, :length]. How do I achieve that, I tried joins and merge but they were no use.
I'd definitely recommend getting this into a query rather than a loop, for efficiency's sake. I think this will work:
Car.joins(:user => :pencils).where("pencils.date = cars.date")
And if you want to reduce it to the array immediately:
Car.joins(:user => :pencils).where("pencils.date = cars.date").pluck("cars.date", "cars.speed", "pencils.length")
If you need to include matches where date is nil, you might need to add:
Car.joins(:user => :pencils).where("(pencils.date = cars.date) OR (pencils.date IS NULL AND cars.date IS NULL)")
Many more efficient options exist, but here is one possible approach:
class User < ActiveRecord::Base
def get_merged_array
dates = (cars.map(&:date) & pencils.map(&:date))
results = []
dates.each do |date|
cars.where(date: date).each do |car|
pencils.where(date: date).each do |pencil|
results << [date, car.speed, pencil.length]
end
end
end
results
end
end
I am looking for a way of designing a Balance model between two users in Ruby on Rails (but could be more general). Two users Alice and Bob would start with a balance of value zero with regards to the other one. Now, if Alice gets a +4, I want Bob to have -4. Here's what I came up with so far:
A balance with three fields : user_one, user_two, and one_to_two. That is if I want the balance for user_one to user_two I just take one_to_two, and if I want from two_to_one I take -one_to_two (just a method in the balance model). The problem is that I could not simply use 'has_many balances through' from the User model, since the balance would need to know if it's user_one or user_two who is calling (I would have to pass the User)
A balance with three fields again, but completely asymmetric: if I create a balance for Alice to Bob, I would have to create another instance of balance from Bob to Alice. This would double the storage needed for each 'balance' in the database, and would require updating the bob_to_alice everytime I update alice_to_bob.
Can anyone come up with something better, or explain the reason for choosing one of the above? Let me know if I can make anything clearer.
So, I think I found something pretty neat. It uses the fact that user alice is always the one with the smallest id (via :ensure_alice_before_bob). The rest should be clear.
The User model just has two extra "has_many :balances_as_alice, has_many :balances_as_bob". It could work with only one (for instance :balances_as_alice), but with those two present + dependent: :destroy you make sure to avoid orphan balances.
# == Schema Information
#
# Table name: balances
#
# id :integer not null, primary key
# alice_id :integer not null
# bob_id :integer not null
# alice_value :float default("0.0")
# created_at :datetime not null
# updated_at :datetime not null
#
class Balance < ActiveRecord::Base
belongs_to :alice, class_name: 'User'
belongs_to :bob, class_name: 'User'
before_validation :ensure_alice_before_bob
validates :alice_id, presence: true
validates :bob_id, presence: true, uniqueness: { scope: :alice_id}
validate :different_users
def value
#for_bob ? -alice_value : alice_value
end
def value=(new_val)
#for_bob? self.alice_value = -new_val : self.alice_value = new_val
end
def Balance.get_for alice, bob
user = 'alice'
alice, bob, user = bob, alice, 'bob' if alice.id > bob.id
balance= alice.balances_as_alice.find_by(bob: bob) ||
Balance.new(bob: bob, alice: alice, alice_value: 0.0)
balance.send("for_#{user}")
end
def for_bob
#for_bob = true; self
end
def for_alice
#for_bob = false; self
end
private
def ensure_alice_before_bob
return if self.alice_id.nil? || self.bob_id.nil?
unless self.alice_id < self.bob_id
self.bob_id, self.alice_id = self.alice_id, self.bob_id
end
end
def different_users
if self.alice_id == self.bob_id
errors.add(:bob_id, "Cannot be the same user")
end
end
end
have model
Class ModelA < ActiveRecord::Base
has_many :apples
before_save :include_prime_apple_in_apples
has_one :prime_apple
def include_prime_apple_in_apples
self.apple_ids << prime_apple_1.id
end
end
l=ModelA.new(:apple_ids => [ "ap_1_id", "ap_2_id"],:prime_apple => prime_apple_1)
l.save
l.apple_ids.should include(prime_apple_1.id) # this doesnt seem to work
How change the params passed for associations?
There's something wrong there but to answer your question:
AFAIK you can only assign to "other_ids". Also you can push directly to the has_many relation:
self.apple_ids = self.apple_ids + [prime_apple_1.id]
or
self.apples << prime_apple_1
Do you have different foreign_key set in the Apple model?
has many will do a select * from apples where modela_id = X
has one will do a select * from apples where modela_id = X limit 1
Which means that whatever you'll set on the prime_apple accessor you'll get back the first Apple record ...