How to rank based on a selection? - ruby-on-rails

For a reservation I would like to calculate the price for extra_guests (e.g. guests not included in the price).
Therefore, I would like to get all the guests for the reservation (reservation_guests). Done
Get their age_table Done
Rank the guests based on their age_tables' column :rank. Issue
The goal is to use the ranking to start calculating the extra_guest_price of the extra_guest with the lowest age_table ranking, in case there are guests not included in the standard reservation.
Issue
I just need a specific range of age_tables to rank based on :rank. My current set-up is wrong as I cannot use order on an array, but I'm not sure how to structure it to make it work.
def total_extra_guest_price(reservation, res_guest)
sum_guest = 0
sum_amount_guests = 0
age_table_list = []
reservation.reservation_extra_guests.each do |guest|
sum_amount_guests += guest.extra_guest_quantity
end
# persons included vs persons reservation
#if more persons
if reservation.room.room_category.persons_included < sum_amount_guests
#count number of too many persons
extra_persons = sum_amount_guests - reservation.room.room_category.persons_included
# iterate over extra_guests belonging to reservation to get age_tables
reservation.reservation_extra_guests.each do |extra_guest_item|
age_table_list << extra_guest_item.extra_guest.age_table
end
#rank age tables, so lowest rank can be used to calculate price. Issue!!!
age_table_list.order(:rank)
end
end

I'm not 100% clear about your question so my suggestion may not be the best solution. But you can try the sort_by()
age_table_list.sort_by{ |age_table| age_table.rank }
read more here

Related

ActiveRecord - Getting the sum of user score

I have an "ITEMS" database made of; ITEM_ID, OWNER_ID and VALUE.
An owner can own unlimited items. An item can only have one owner.
And I have an "OWNER" database made of; ID, NAME
I want to find the NAMES of top 10 RICHEST (wealthier) people. How can I do that?
First, I need to sum the values of an owner_id; than compare that with others?
Here is what you can do :
Item.group(:owner_id) # grouping Items by owner id
.select("SUM(value) as sum") # summing values of each group
.order("sum DESC") # ordering resulting records by the sum value
.limit(10) # giving the top 10 records
It is kinda long solution but it worked for me:
toplist = []
all_owners = Owner.all
all_owners.each do |owner|
name = Owner.find(owner).name
owner_value = Item.where(owner_id: owner).sum(:value)
toplist << [owner_value,name]
end
#top10 = toplist.sort.last(10).reverse

Comparing two users

I want to compare a user profile with another user profile based on education, experiences, skills, and country. If education is similar, the score will be 20, else it will be 0. And total_score will be the sum of the scores. I can then sort profiles similar to that of a given user based on the total score. Can anyone help me on how I can achieve this?
I don't know how to compare two users or two things from the same table, and I don't know where to put my code in: the profile model, helper, or what.
Add this to your User model.
def total_score
education+skills+country #modify to determine score, idk how you plan to determine this
end
When trying to sort:
User.all.sort_by{|a| a.total_score} #returns a list of users sorted by total score.
EDIT
I think you mean this:
def total_score
score=0
if education=="First School"
score+=10
elsif education=="Second School"
score+=5
end
if skill=="Skill 1"
score+=10
elsif education=="Skill 2"
score+=5
end
if country=="Country 1"
score+=10
elsif country=="Country 2"
score+=5
end
end

Act_As_Votable with Reddit style weighting algorithm in Rails

I am creating a rails app that has a User and Post model that implements the Act_As_Votable gem.
I want users to be able to upvote and downvote posts, but also want to rank and sort posts by a weighted_score algorithm that takes into account the number of upvotes, downvotes, and time the post was created.
My weighted_score algorithm is taken from Reddit and better described here.
My Post Model:
class Post < ActiveRecord::Base
belongs_to :user
acts_as_votable
# Raw scores are = upvotes - downvotes
def raw_score
return self.upvotes.size - self.downvotes.size
end
def weighted_score
raw_score = self.raw_score
order = Math.log([raw_score.abs, 1].max, 10)
if raw_score > 0
sign = 1
elsif raw_score < 0
sign = -1
else
sign = 0
end
seconds = self.created_at.to_i - 1134028003
return ((order + sign * seconds / 45000)*7).ceil / 7.0
end
end
I want to use the Acts_As_Voteable gem because it supports caching which may decrease the number of hard disk writes and save time. Currently the weight_score of a post can be calculated on the fly but is not saved in the database, meaning I cannot do database sorts on posts with the highest weighted_score.
If I created a column in the post model I would have to update the posts table every time a user voted on a post, which defeats the purpose of using the Acts_As_Tagable gem (as I don't take advantage of its caching ability).
So I want to add a column to the votes table to store the weighted_score (which will then be calculated every time the post is voted on), as well as a method to the Votes model to calculate this score, however the gem does not provide a model when I run its generator. It only creates a votes table which I do not know how to access without a model.
Any help on how I can add such a weighted_score column and method to the votes model, or on how to achieve efficiently storing the weighted score of a post in a different manner is appreciated.
acts_as_voteable adds methods to your model to access the votes
http://juixe.com/techknow/index.php/2006/06/24/acts-as-voteable-rails-plugin/
positiveVoteCount = post.votes_for
negativeVoteCount = post.votes_against
totalVoteCount = post.votes_count
If you want to add a column, you can run a migration as normal on the table it creates. It also does appear to create a Vote model http://juixe.com/svn/acts_as_voteable/lib/vote.rb
I would add the weighted_score column to your Post model and handle updating via callback. For instance:
class Post < ActiveRecord::Base
#...
before_save :update_weighted_score
#...
def update_weighted_score
# check if some relevant variables have changed first, for example
if cached_votes_total.changed?
# do maths
weighted_score = blah
end
end
You can do this with MYSQL out of the box with decent results, used multi-line for easier readability.
Post.order("
LOG10( ABS( some_score ) + 1 ) * SIGN( some_score ) +
( UNIX_TIMESTAMP( created_at ) / 450000 ) DESC
")
450000 is the number to tweak that will given more weighting to the score vs. the created_at.
Closer to zero gives more weight to the new-ness.
45000 will roughly return scoring for the day
450000 will roughly return scoring for the week
4500000 will roughly return scoring for the month

If same score then same rank in RAILS 3.2

Hi I have a ranking system wherein if they have same score or points then both users should have same rank.
I am getting it thru the index, but can't manage to make their indexes both equal if they have same score
user.rb
def get_rank
x = User.get_rank.index(self)
x ? (x + 1) : x
end
def self.get_rank
Response.joins(:answer).where("answers.correct is TRUE").map(&:user).uniq.sort_by(&:score).reject{|me| me.super_admin or me.questions.count < Question.count}.reverse
end
How can I make the users who have same scores to have just 1 similar rank.
E.g. if both users get 25 points, and 25 is the highest from the postings, then they must have the first rank.
Any workarounds will be appreciated
The question is rather confusing but I think you could make better use of the database functions. Maybe something like this works, since I don't know your full models, especially which object has the score of the user. I'm assuming its on the user object:
def get_rank
scores = User.select(:score).joins(:response, :answers).where(:answers => [:correct => true]).order('score DESC').group(:score).all
# example result: [24, 22, 21, 20 ...]
rank = scores.index(score) + 1
end
The result of that statement gives you a sorted array of all actually applied scores. Since you do know the current user's score, you can get the index of that score, which is also the rank number.

Filter order needing changes for invoice calculation?

Picture a normal invoice: On it, you have several items. Each item has a quantity and a price per unit, among other things (unit and description).
The total amount for each item is calculated like this: quantity * price per unit.
This is done for each item. Then, the overall invoice's net amount is the sum of all the totals. Add VAT, and you have the invoice's gross amount.
This is what I am trying to do with my Rails app. An invoice has many items and accepts nested attributes for them. Generally, this all works fine.
Following the logic, all I need to enter in manually is the price per unit and the quantity for each item as well as the invoice's VAT. The totals and the resulting net and gross amount should be calculated automatically. I want to achieve this using the before_save filter.
Here is my invoice model:
before_save :calculate_net_amount, :calculate_gross_amount
def calculate_net_amount
self.items do |item|
self.net_amount += item.total
end
end
def calculate_gross_amount
self.gross_amount = self.net_amount * (1 + self.vat_rate)
end
This is the item model:
before_save :calculate_total
def calculate_total
self.total = self.quantity * self.price_per_unit
end
And here is my spec that is failing:
it "calculates the net amount from all item totals" do
invoice = FactoryGirl.build(:invoice)
item = invoice.items.build(quantity: 2, unit: "Unit", description: "Desc", price_per_unit: 2)
invoice.save
invoice.net_amount.should == 4
end
It uses this invoice factory:
FactoryGirl.define do
factory :invoice do
association :client
currency "EUR"
vat_rate 0.19
net_amount 1
payment_on "2013-01-01"
status "paid"
end
end
The test basically does the following: An invoice with 2 things that both cost USD 2 should have a net amount of USD 4. Instead, the test returns 1, which seems to come from the factory and apparently isn't overwritten. If I remove it from the fixture, it says that it cannot calculate the gross amount anymore, since it can't use * on any nil object.
I am assuming I am doing something wrong with the filters and the order in which they are called - the total amounts are calculated correctly, so it has to be something about the calculate_net_amount method that's going wrong and as a result it can't calculate the gross amount anymore.
Can you see what I am doing wrong?
self.items do |item|
should be
self.items.each do |item|
Since items is an accessor, which is a method, it can technically take a block, but that block isn't getting called, so no summing is happening. It's an easy typo to make.
As an aside, it's better is to sum using inject:
self.net_amount = self.items.inject(0){|sum, item| sum + item.total}

Resources