Better method to count an associated object off a group of objects - ruby-on-rails

How would one count the number of Items that are tickets in the following scenario? The ticket column lives in the Items table.
I'm trying to grab a group of redemptions, and then count the number of items that are 'tickets' for that group of redemptions.
class Item < ActiveRecord::Base
has_many :redemptions
class Redemption < ActiveRecord::Base
belongs_to :item
#This method works, but is there a much better way?
def tickets_sold
my_tickets_sold = 0
#redemptions = Redemption.where(state: "valid")
redemptions.each do |redemption|
if redemption.item.ticket == true
my_tickets_sold = my_tickets_sold + 1
end
end
my_tickets_sold
end

Yes you can do it in better way, you can use below of the 3 ways
1) Add associans for getting records of sold tickets
has_many :sold_tickets, -> { left_outer_joins(:item).where("state = 'valid' AND items.ticket = true ") }
2) Add scope for fetching count of sold tickets
scope :sold_tickets_count, -> { left_outer_joins(:item).where("state = 'valid' AND items.ticket = true ").count }
3) Add instance method in model for fetching count of sold tickets
def sold_tickets_count
left_outer_joins(:item).where("state = 'valid' AND items.ticket = true ").count
end

Related

How To Fix undefined method `product' for #<LineItem::ActiveRecord_Relation:0x0000000017b22f70>

I'm try product quantity - 1 but ı get this error
line_item.rb
belongs_to :order
belongs_to :product
payment.rb
has_many :orders
undefined method `product' for # LineItem::ActiveRecord_Relation:0x0000000017b22f70>
#line_item = LineItem.where(:order_id => params[:zc_orderid])
#line_item.product.quantity = #line_item.product.quantity - 1
if #line_item.product.quantity == 0
#line_item.product.sold = true
end
#line_item.product.save
If you use where, you don't get a single LineItem object, but a LineItem::ActiveRecord_Relation object. If that condition is enough to get just one record then use find_by. If it's not you need to think more about the logic because you'd get more than one object.
#line_item = LineItem.find_by(:order_id => params[:zc_orderid])
If you want to decrease the quantity of all those line items I'd do something like
LineItem.transaction do
LineItem.where(:order_id => params[:zc_orderid]).each do |line_item|
line_item.product.quantity = line_item.product.quantity - 1
if line_item.product.quantity == 0
line_item.product.sold = true
end
line_item.product.save
end
end
Since Order has many LineItem you should expect more than one line, so should rewrite your code:
LineItem.where(:order_id => params[:zc_orderid]).each do |line_item|
product = line_item.product
product.quantity -= 1
if product.quantity == 0
product.sold = true
end
product.save
end
Btw, consider add a Transaction.
LineItem.where(:order_id => params[:zc_orderid]) its return as array format.
So you can fetch by following
LineItem.find_by(order_id: params[:zc_orderid]). its return single active record

Counting percentage of true elements in ruby

class Constituency < ActiveRecord::Base
has_many :votes
end
class Vote <ActiveRecord::Base
belongs_to :constituency
end
I have a database with votes and each vote has boolean attribute "valid_vote".
What I want to receive how many votes are in each constituency (in percent), but counting only valid votes. So 100% is all votes, where valid_vote == true.
Any ideas how should I write it?
To get all the percentage of valid votes to all votes run:
all_votes_count = constituency.votes.count
valid_votes_count = constituency.votes.where(valid_vote: true).count
if all_votes_count > 0
percent = valid_notes_count / all_votes_count
else
puts "no votes"
end
You can do it with a single SQL query like this:
h = votes.group(:valid_vote).count
percentage = 100.0 * h[true] / h.values.sum rescue 0

rails 4 - sort by number of votes

So... I have images. and those images have votes.
I currently have image.rb
class Image < ActiveRecord::Base
belongs_to :user
belongs_to :event
has_many :image_votes, dependent: :destroy
default_scope { order(ci_lower_bound) }
def taken_by? (photographer)
self.user == photographer
end
def self.ci_lower_bound
pos = image_votes.where(value: 1).size
n = image_votes.size
if n == 0
return 0
end
z = 1.96
phat = 1.0*pos/n
(phat + z*z/(2*n) - z * Math.sqrt((phat*(1-phat)+z*z/(4*n))/n))/(1+z*z/n)
end
end
I've been playing around with this... the only way to get default scope to work is to use a method with self. I found that formula at http://www.evanmiller.org/how-not-to-sort-by-average-rating.html - how would I call this to make it work??
I'd create a new scope called by_votes, include sum() and order by this new column:
scope :by_votes, -> { select("#{Image.table_name}.*, sum(#{ImageVote.table_name}.votes) AS votes").order("votes DESC") }

How do I include the joined record in the result using squeel

st = 'pen'
ak = '123123'
agreements = Client.where{authentication_key == ak}.first.agreements
products = Product.joins{agreements}.where{agreements.id.in(a) & (short_description.like(st) | long_description.like( st))}
I am trying with the above but I need the matched agreement in my result set too..
Because of this
class Product < ActiveRecord::Base
has_and_belongs_to_many :agreements, uniq: true
I cant use products.first.agreement.first.... That could be a different agreement.
I'm not sure if this is exactly what you need, but hopefully it will be:
client = Client.where { authentication_key == ak }.first
products = Client.agreements.joins { products }.where { (short_description.like(st) | long_description.like(st) }
This returns the products associated with the agreements which are, in turn, associated to your client. If you need to get one client's agreements for a product, you could write a scope:
class Product < ActiveRecord::Base
#...
def self.agreements_for_client(id)
joins { agreements }.where { client_id.eq(id) }
end
end
and the call it:
client = Client.where { authentication_key == ak }.first
Product.first.agreements_for_client(client.id)
I hope this helps.

How do I return all non deleted messages of a current thread in ruby on rails?

Having a little trouble figuring out a way to display messages for users message threads depending on which ones have been deleted and not. Deleted messages "sender_status or recipient_status" will change from 0 to 1 on deletion of a message. Currently to display a list of users message threads in their inbox I use:
e.g.
current_user = User.find(2)
current_user.message_threads
This grabs all their message_threads with 0 sender or recipient statuses.
class User < ActiveRecord::Base
has_many :messages
has_many :message_threads
Nice and easy but what I would like to do now is some how set up something that enables me to be able to grab messages with 0 sender or recipient statuses from a current thread. So if I was to type:
one_thread = current_user.message_threads.first
From this thread I'd want to be able to easily grab the only messages I needed. Would like to put something in the message_thread model so I could eventually type out:
current_user.message_threads.first.messages
#or
one_thread.messages
and have only the messages I needed loaded and the ones with "1" statuses ignored.
User Model:
class User < ActiveRecord::Base
has_many :messages
has_many :message_threads
def message_threads
MessageThread.where([ '(sender_id = ? AND sender_status = 0) OR (recipient_id = ? AND recipient_status = 0)', self.id, self.id ])
end
def messages
Message.where([ '(sender_id = ? AND sender_status = 0) OR (recipient_id = ? AND recipient_status = 0)', self.id, self.id ])
end
MessageThread model:
class MessageThread < ActiveRecord::Base
has_many :messages
Message model:
class Message < ActiveRecord::Base
acts_as_tree
has_one :message_thread
I tried experimenting with joins but this didn't work out well for me.
def messages
joins(:messages).merge( Message.where([ '(parent_id = ? AND sender_status = 0) OR (parent_id = ? AND recipient_status = 0)',self.message_id, self.message_id ]) )
end
Help would be much appreciate.
Kind regards
I'm guessing the problem is that one_thread is returning a MessageThread object, and then you're trying to get the user id by calling self.id, but it's being called on the wrong model.
I haven't tried this code but it might work:
class User < ActiveRecord::Base
def self.threads_in_inbox
message_threads.where('(sender_id = ? AND sender_status = 0) OR (recipient_id = ? AND recipient_status = 0)', self.id, self.id)
end
end
class MessageThread < ActiveRecord::Base
def self.inbox_messages
messages.where('(sender_id = ? AND sender_status = 0) OR (recipient_id = ? AND recipient_status = 0)', self.user.id, self.user.id)
# this hits the database 1 extra time however due to self.user.id
# you could change it to inbox_messages(user_id) and pass in the id manually
end
end
first_thread = current_user.threads_in_inbox.first
first_thread.inbox_messages
In your question it seems like you're overriding the association queries generated by rails (message_threads and messages) - not sure if it's on purpose but I changed it above.
Also, I think you can remove the duplication and do the following:
where('(sender_id = ?) AND (sender_status = 0 OR recipient_status = 0)', self.id)

Resources