Convert array of objects into number for inject sum - ruby

I am using Ruby 1.9.2, Rails 3.1. I have the following:
# review.rb
def calculate_rating
all_rating = Review.select("rating").where("reviewable_id = ?", self.reviewable_id)
all_rating.inject(:+)
end
# reviews_controller.rb
def create
#reviewable = find_reviewable
#review = #reviewable.reviews.where("user_id = ?", current_user).first
if #review.save
#review.calculate_rating
redirect_to :id => nil
else
flash[:error] = 'Error saving review. Please try again.'
redirect_to :id => nil
end
end
The idea behind this is that when a new review with rating is submitted and saved, it will find all ratings for all #reviewable, sum all the ratings and divide by the total number of ratings.
Problem that I am facing currently is this line: all_rating = Review.select("rating").where("reviewable_id = ?", self.reviewable_id) where all_rating returns an array of objects, like below:
[#<Review rating: #<BigDecimal:1050f0a40,'0.3E1',9(18)>>, #<Review rating: #<BigDecimal:1050f0928,'0.1E1',9(18)>>]
which I can't do any arithmetic calculation to it. I need it to be an array of numbers before I could use the inject to sum it and divide by the number of ratings.
Please advise me how I can get the inject to work. Many thanks!

AR/SQL (faster):
Review.select("rating").where(:reviewable_id => self.reviewable_id).sum(:rating)
Ruby (slower):
Review.select("rating").where(:reviewable_id => self.reviewable_id).map(&:rating).sum

How about just doing this:
def calculate_rating
all_rating = Review.select(:rating).where(:reviewable_id => reviewable_id).map(&:rating)
all_rating.inject(:+) # or you could just do all_rating.sum
end

Related

rails sort collection by custom method

I have a method which takes ID of project and find all payments, do the math and the output is percentage of success (target amount vs collected)
def projectCollectedMoneyPerc(id)
#project= Project.find(id)
#collected = Payment.where('project_id = ? and confirmed = true', #project.id)
#col = #collected.sum(:amount)
#perc = ((#col.to_f / #project.amount) * 100).round(0)
end
now I need to find projects which have most % success. My idea was to call this method by sort_by but I have no idea how to put ID from collection to this sort
my collection is simple
#projects=Project.where('enabled = true and enddate > ?', Time.now)
thanks
I would define a method like to following in your model:
# in app/models/project.rb
has_many :payments
def collected_money_percentage
sum = payments.where(confirmed: true).sum(:amount)
(100.0 * sum / amount ).round
end
Then you cound use that method like this:
Project.where('enabled = true and enddate > ?', Time.now)
.sort_by(&:collected_money_percentage)
Please note that this first loads all matching record and then calculations the percentage in memory. It would probably be faster to calculate this values in your database:
Project.joins(:payments)
.where('enabled = true and enddate > ?', Time.now)
.group('projects.id')
.order('SUM(payments.amount) / projects.amount')

Can mechanize for Ruby filter the contents of a <select> by class?

Sorry, but I didn't find the documentation enlightening at all. Basically, I am trying to iterate through a where some options are not valid. The ones I want have 'class="active"'. Can I do that with mechanize? Here's what I have so far:
class Scraper
def init
mech = Mechanize.new
page = mech.get('url')
#Now go through the <select> to get product numbers for the different flavors
form = page.form_with(:id => 'twister')
select = form.field_with(:name => 'dropdown_selected_flavor_name')
select.options.each do |o|
if (o.text != "")
value = o
end
productNumber = trim_pn(value.to_s[2..12])
puts productNumber
end
end
#Checks validity of product number and removes excess characters if necessary
def trim_pn(pn)
if (pn[0] == ",")
pn = pn[1..-1]
end
return pn
end
end
p = Scraper.new
p.init
All that does is grabs the product number and removes some extra info that I don't want. I thought replacing the .each do with this:
select.options_with(:class => 'active').each do |o|
if (o.text != "")
value = o
end
end
But that throws "undefined method 'dom_class' for Mechanize:Form:Option blah blah." Is there are different way I should be approaching this?

Why do I get a `syntax error, unexpected tCONSTANT, expecting keyword_end`?

I want to check the price of a certain product on a certain webshop.
I'm using a constant to store a Hash of webshop data so editing is easier (more stores will be added).
Here's the code I'm using:
require 'httparty'
require 'nokogiri'
class Prijscheckr
STORES = {
:zara => {
'base_uri' => 'http://www.zara.com/nl/',
'normal_price_css' => 'p.price > span',
'css_normal_price_extract' => "[0].attr('data-price')",
'normal_price_xpath' => '/p[3]/span',
'xpath_normal_price_extract' => "[0].attr('data-price')"
}
}
def begin(args = {})
page = Nokogiri::HTML(HTTParty.get(args[:url]))
price = page.css(STORES[:zara]['normal_price_css'])STORES[:zara]['css_normal_price_extract']
end
end
When doing
p = Prijscheckr.new
p.begin(url: 'http://www.zara.com/nl/nl/collectie-aw14/dames/jacks/leren-bikerjack-c269184p2137577.html')
Here are the results:
# Works
# price = page.css('p.price > span')[0].attr('data-price')
# Works
# price = page.css(STORES[:zara]['normal_price_css'])[0].attr('data-price')
# Does not work
# price = page.css(STORES[:zara]['normal_price_css'])STORES[:zara]['css_normal_price_extract']
How can I concatenate price = page.css(STORES[:zara]['normal_price_css'])STORES[:zara]['css_normal_price_extract'] without hard coding it in the method?
Ruby code cannot be created by concatenation of string. You might like to declare css_normal_price_extract as a lamba
'css_normal_price_extract' => ->(val) {val[0].attr('data-price')}
price = STORES[:zara]['css_normal_price_extract'].call(page.css(STORES[:zara]['normal_price_css']))

Datamapper into String

I want to be able to see the string like the TwitchTV name I have in my database. Here is my current code
get '/watch/:id' do |id|
erb :mystream
#result = Twitchtvst.all( :fields => [:Twitchtv ],
:conditions => { :user_id => "#{id}" }
)
puts #result
end
result in terminal;
#< Twitchtvst:0x007fb48b4d5a98 >
How do I get that into a string (TwitchTV answer in database)
Opppppsss!
Here is the real code sample. Sorry!
get '/livestream' do
erb :livestream
#users_streams = Twitchtvst.all
puts #users_streams
end
If I add .to_s at users_stream it does not work
By adding .to_csv, not exactly a string, but it should show the content:
get '/livestream' do
erb :livestream
#users_streams = Twitchtvst.all
#users_streams.each do |us|
p us.to_csv
end
end
You're getting a Collection of Twitchtvst objects, so you need to convert each to a String:
puts Twitchtvst.all.map(&:to_s).join

Rails 3 way working with multiple if statements and form create/new

I have a problem to approach and not sure what the most appropriate method will be to make this work. Here the background to begin:
There are two models I am working with Procedures and Appointments. The Appointments model belongs_to the Procedures model and Procedures model has_many Appointments.
Now on the procedures model there are two key points to focus on, rather, two key columns.
attr_accessible :visits, :occurence
visits is the specific number of times to schedule the Appointment(s).
occurence is the frequency of the visits. An example would be visits: "5", occurence: "weekly"
So when I am submitting my form I would like to write a method that looks at both visits: "x" and occurence: ["weekly", "biweekly", "monthly"] to then create a if or a switch -- php does switch still looking into ruby version -- but I suspect there is an elegant way to write this up.
My current create method looks like this:
def create
#appointment = Appointment.new(params[:appointment])
set_variables
if #appointment.save
flash[:success] = "Appointment scheduled!"
redirect_to patient_path(#current_patient)
else
redirect_to patient_path(#current_patient)
flash[:error] = "Appointment Date and Time cannot be blank, please try again."
end
end
What would be the best way to tackle a) identifying occurence: ["weekly", "biweekly", "monthly"] and then processing visits: "x" based on something similar to:
if #appointment.occurence == "weekly"
(x-1).times do |n|
submit same params but change within params appointment_date: = ((#appointment.appointment_date) + (n+1).week.to_formatted_s(:db)
#appointment.save
end
end
...and so on and so forth using (n+1).month for monthly occurrence (n+2).day and for bi-weekly occurrence(s).
Thank you in advance, hope this clarifies things. Just one item to note, do I need to store in database visits: and occurence:, I suspect not but would like to be certain they are used when hitting the models_controller create function.
Here's a slightly less cluttered solution that should do what you need, though it also assumes that you get rid of the :appointment_date field and change :appointment_time to a DateTime field. For more info on DateTimes check out:
(Stackoverflow will only allow me to post 2 links because I'm a n00b so search "DateTime Ruby" on your favorite search engine for documentation for the Ruby and rails methods for DateTime)
Formatting DateTime to string for views: http://apidock.com/ruby/DateTime/strftime
Intro on using DateTimes in forms: http://guides.rubyonrails.org/form_helpers.html#using-date-and-time-form-helpers
#appointment = Appointment.new(params[:appointment])
set_variables
if #appointment.save
if #procedure.occurence == "WEEKLY"
multiplier = 7
elsif #procedure.occurence == "BIWEEKLY"
multplier = 14
else
multiplier = 30
end
#visits = #procedure.visits - 1
#visits.times do |n|
Appointment.create!(
:procedure_id => #appointment.procedure_id,
:patient_id => #appointment.patient_id,
:appointment_time => (#appointment.appointment_time + (multiplier * n).days),
:attendance => "SCHEDULED"
)
end
else
flash.now[:error] = "There appears to be an error, please try again."
render 'new'
end
Solved for the moment, rather crude--as is my current ruby skill set--but it seems to have done the job.
#appointment = Appointment.new(params[:appointment])
set_variables
#appointment.save
if #procedure.occurence == "BIWEEKLY"
#visits = #procedure.visits - 1
#visits.times do |n|
procedure_id = #appointment.procedure_id
patient_id = #appointment.patient_id
appointment_date = (#appointment.appointment_date +
((n+2)*2).days).to_formatted_s(:db)
appointment_time = #appointment.appointment_time
appointment_notes = #appointment.appointment_notes
attendance = "SCHEDULED"
#scheduled = Appointment.create(procedure_id: procedure_id,
patient_id: patient_id, appointment_date: appointment_date,
appointment_time: appointment_time,
appointment_notes: appointment_notes, attendance: attendance)
end
end
if #procedure.occurence == "WEEKLY"
#visits = #procedure.visits - 1
#visits.times do |n|
procedure_id = #appointment.procedure_id
patient_id = #appointment.patient_id
appointment_date = (#appointment.appointment_date +
(n+1).week).to_formatted_s(:db)
appointment_time = #appointment.appointment_time
appointment_notes = #appointment.appointment_notes
attendance = "SCHEDULED"
#scheduled = Appointment.create(procedure_id: procedure_id,
patient_id: patient_id, appointment_date: appointment_date,
appointment_time: appointment_time,
appointment_notes: appointment_notes, attendance: attendance)
end
end
if #procedure.occurence == "MONTHLY"
#visits = #procedure.visits - 1
#visits.times do |n|
procedure_id = #appointment.procedure_id
patient_id = #appointment.patient_id
appointment_date = (#appointment.appointment_date + (n+1).month).to_formatted_s(:db)
appointment_time = #appointment.appointment_time
appointment_notes = #appointment.appointment_notes
attendance = "SCHEDULED"
#scheduled = Appointment.create(procedure_id: procedure_id,
patient_id: patient_id, appointment_date: appointment_date,
appointment_time: appointment_time,
appointment_notes: appointment_notes, attendance: attendance)
end
end

Resources