calculate number in views ruby on rails - ruby-on-rails

I would like to calculate mid test and final test mark into a grade. E.g.:
mid test = 80 , final test = 80
(midtest + finaltest)/2 >=80
grade = "A"
Is it possible to do an if condition in views and insert into database? Something like:
if (midtest + finaltest) / 2 >= 80
grade = "A"
elsif (midtest + finaltest)/2 >= 70 and < 80
grade = "B"
elsif (midtest + finaltest) /2 >= 60 and < 70
grade = "C"
So that in views we don't need a text_field for grades and so that the calculation is automatically inserted into the database.
This is the solution
Controller
def create
#nilai = Nilai.new(params[:nilai])
#nilai.get_grade
respond_to do |format|
if #nilai.save
format.html { redirect_to #nilai, notice: 'Nilai was successfully created.' }
format.json { render json: #nilai, status: :created, location: #nilai }
else
format.html { render action: "new" }
format.json { render json: #nilai.errors, status: :unprocessable_entity }
end
end
end
Model
class Nilai < ActiveRecord::Base
attr_accessible :grade, :id_makul, :id_mhs, :id_nilai, :uas, :uts
def get_grade
#calculate = (self.uas + self.uts)/2
if #calculate >= 80
self.grade = "A"
elsif #calculate >=70 and #calculate < 80
self.grade = "B"
elsif #calculate >=60 and #calculate <70
self.grade = "C"
elsif #calculate >=50 and #calculate <60
self.grade = "D"
else
self.grade = "E"
end
end
end

Still guessing what you really want and why you think you have to do it in the view...
As I said above, Views should be used solely for code that displays data that already exists. Code that inserts things into the database is for your models and controllers.
I suggest either:
1) you create a method on your model called "grade" eg:
def grade
if (midtest + finaltest) / 2 >= 80
return "A"
elsif (midtest + finaltest)/2 >= 70 and < 80
return "B"
elsif (midtest + finaltest) /2 >= 60 and < 70
return "C"
else
return "F"
end
end
now, you can call this method from your view eg:
Grade: <%= #my_model.grade %>
Note that this method does not insert it into the database.
OR
2) you create a method as above on a before_save callback
eg lets say you're storing it into the "grade" column in the db:
class MyModel < ActiveRecord::Base
before_create :calculate_grade
def calculate_grade
if (midtest + finaltest) / 2 >= 80
self.grade = "A"
elsif (midtest + finaltest)/2 >= 70 and < 80
self.grade = "B"
elsif (midtest + finaltest) /2 >= 60 and < 70
self.grade = "C"
else
self.grade = "F"
end
end
end
And now whenever your model gets saved, the grade gets re-calculated from the test scores and saved into the db alongside.
so you can use "grade" in your views as above, but it's coming from the database column
Grade: <%= #my_model.grade %>

technically you could do almost anything in views, after all view is also a ruby file. So you could have conditions, DB connections etc..
BUT, Its not a good practise to have your logic in your views. Always try to have your view to display only. All the processing logic should be in Models
Idea is, Fat models, thin controllers are views are only for presentation. So in your case try and see at least to get your login in to a helper method.
One more thing I notice, you could have this line
(midtest + finaltest) / 2
to
average_marks = (midtest + finaltest) / 2
and use average_marks, in other places, as its more DRY (Dont Repeat Yourself)
HTH :)

Related

Rails survey calculating results and storing results in DB

Having trouble with the controller of my survey (Waterusage). It has 30+ variables collected from a form and those inputs need to be saved to the waterusage db and used to calculate a final score, also saved in the database.
class Waterusage < ApplicationRecord
belongs_to :user
end
class WaterusagesController < ApplicationController
def new
#waterusage = Waterusage.new
end
def create
#user = User.find(params[:user_id])
_showerTotal = :average_shower * :shower_flow_rate * :household_size
_bathTotal = :bath_rate * :bath_multiplier * 35
_bathroomSinkTotal = :bathroom_sink_usage * :bathroom_sink_flow_rate * :household_size
_toiletTotal = :mellow * :low_flow_toilet * :household_size
_kitchenTotal = :kitchen_sink_usage * :kitchen_sink_flow_rate
_dishwashingTotal = :dishwasher_rate * :dishwasher_multiplier * :dishwasher_method
_laundryTotal = :laundry_rate * :laundry_method * :laundry_multiplier
_homeUsage = _showerTotal + _bathTotal + _bathroomSinkTotal + _toiletTotal + _kitchenTotal + _dishwashingTotal + _laundryTotal + :greywater
_lawnTotal = :lawn_rate * :lawn_multiplier * :lawn_size * :xeriscaping
_swimmingTotal = (:swimming_pool / 365) + (:swimming_months * 1000 / 365
_carwashTotal = :carwash_rate * :carwash_multiplier * :carwash_method
_outsideUsage = _lawnTotal + _swimmingTotal + _carwashTotal
_drivingTotal = 0.735 * :miles
_powerTotal = :statewater * :percent_statewater / 100
_indirectTotal = :shopping + :paper_recycling + :plastic_recycling + :can_recycling + :textile_recycling + :diet + (200 * :pet_cost / 30)
:household_total = _homeUsage + _outsideUsage + _drivingTotal + _powerTotal + _indirectTotal
:individual_total = :household_total / :household_size
#waterusage = #user.waterusage.create(waterusage_params)
redirect_to user_path(#user)
end
def destroy
#user = User.find(params[:user_id])
#waterusage = #user.waterusage.find(params[:id])
#waterusage.destroy
redirect_to user_path(#user)
end
private
def waterusage_params
params.require(:waterusage).permit(:household_size, :average_shower,
:shower_flow_rate, :bath_rate, :bath_multiplier, :bathroom_sink_usage,
:bathroom_sink_flow_rate, :mellow, :low_flow_toilet, :kitchen_sink_usage,
:kitchen_sink_flow_rate, :dishwasher_rate, :dishwasher_multiplier,
:dishwasher_method, :laundry_rate, :laundry_multiplier, :laundry_method,
:greywater, :lawn_rate, :lawn_multiplier, :lawn_size, :xeriscaping,
:swimming_pool, :swimming_months, :carwash_rate, :carwash_multiplier,
:carwash_method, :miles, :statewater, :percent_statewater, :shopping,
:paper_recycling, :plastic_recycling, :can_recycling, :textile_recycling,
:diet, :pet_cost, :individual_total, :household_total)
end
end
Is there a better way I can be doing this? Currently there are errors on the lines that are working to sum subtotals. (ie. :household_total = _homeUsage + _outsideUsage + _drivingTotal + _powerTotal + _indirectTotal
)
Also I'm not sure if I am properly connecting the user info to the survey schema
You don't want to do that math in the controller. Skinny controllers, fat models. Aside from that, one reason it's failing is that the syntax is incorrect. A symbol (:hello_world) can't be assigned a value nor does it contain one. Less importantly, while it's not illegal to have an underscore prefixed local variable, that is not the convention in Ruby. Neither is camelcase. You want hello_world rather than helloWorld. Anyway...
Assumption: You have a requirement that the totals must be persisted. They cannot be calculated values.
You want to move those calculations to the model. And instead of assigning a ton of variables, use methods. That way you can easily unit test them.
What's missing here: Validations in the model that ensure that all expected attribute values are present. The controller should handle an invalid Waterusage instance on create, too. This code is untested and is just for illustrative purposes.
class Waterusage < ApplicationRecord
belongs_to user
before_validation :calculate_totals
def calculate_totals
self.household_total = get_household_total
self.individual_total = get_individual_total
end
def get_household_total
home_usage + outside_usage + driving_total + power_total + indirect_total
end
def get_individual_total
household_total / household_size
end
def home_usage
shower_total + bath_total + bathroom_sink_total + toilet_total + kitchen_total + dishwashing_total + laundry_total + greywater
end
def outside_usage
lawn_total + swimming_total + carwash_total
end
def driving_total
0.735 * miles
end
def power_total
statewater * percent_statewater / 100
end
def indirect_total
shopping + paper_recycling + plastic_recycling + can_recycling + textile_recycling + diet + (200 * pet_cost / 30)
end
def shower_total
average_shower * shower_flow_rate * household_size
end
def bath_total
bath_rate * bath_multiplier * 35
end
def bathroom_sink_total
bathroom_sink_usage * bathroom_sink_flow_rate * household_size
end
def toilet_total
mellow * low_flow_toilet * household_size
end
def kitchen_total
kitchen_sink_usage * kitchen_sink_flow_rate
end
def dishwashing_total
dishwasher_rate * dishwasher_multiplier * dishwasher_method
end
def laundry_total
laundry_rate * laundry_method * laundry_multiplier
end
def lawn_total
lawn_rate * lawn_multiplier * lawn_size * xeriscaping
end
def swimming_total
(swimming_pool / 365) + (swimming_months * 1000 / 365)
end
def carwash_total
carwash_rate * carwash_multiplier * carwash_method
end
end
class WaterusagesController < ApplicationController
...
def create
#user = User.find(params[:user_id])
#waterusage = #user.waterusage.create(waterusage_params)
redirect_to user_path(#user)
end
...
end
First of all prefix every ':' inside of create with a 'params[' and suffix ']', then change every '_' with a '#'.
It would be like this:
_powerTotal = :statewater * :percent_statewater / 100
turns into
#powerTotal = params[:statewater].to_i * params[:percent_statewater].to_i /100
Like that,
:individual_total = :household_total / :household_size
turns into
#individual_total = params[:household_total].to_i / params[:household_size].to_i
Also you're doing nothing with your calculations, they are just floating around, as it is, you can't even invoke them from your view.
If you want it to save on your waterusage object that relates to an user the individual_total attributes it would be;
#waterusage = #user.waterusage.create(waterusage_params, individual_total: #individual_total).

NoMethodError in controller

Hello I'm new at Ruby and I'm trying to make a method in my Project controller like so:
def update_phase
#project = Project.find(params[:id])
diff = (Date.current.year * 12 + Date.current.month) - (#project.starting.year * 12 + #project.starting.month)
case
when diff >= 30
#project.process = 11
.
.
.
when diff >= 0
#project.process = 1
else
#project.process = 0
end
proc = #project.process.to_f
case
when proc >= 9
#project.phase = "Final"
when proc >= 5
#project.phase = "Desarrollo"
when proc >= 1
#project.phase = "Inicio"
else
#project.phase = "Error en el proceso"
end
end
starting is a timestamp in the model. In my view I have:
<% #project.update_phase %>
but I get the error: "NoMethodError in Projects#show"
how can I fix this?
Depending on what's or where does starting come from, you could use a before_save callback, this way everytime you're going to create a new record, it triggers the update_phase method and assigns the values for process and phase from the current project object:
class Project < ApplicationRecord
before_save :update_phase
...
def update_phase
diff = (Date.current.year * 12 + Date.current.month) - (self.starting.year * 12 + self.starting.month)
case
when diff >= 30
self.process = 11
...
end
proc = self.process.to_f
case
when proc >= 9
self.phase = 'Final'
...
end
end
end

How do I convert this string to milliseconds?

I’m using Rails 4.2.7. I want to calculate the number of milliseconds given a duration, which could include hours, minutes, and seconds. So I wrote these two functions:
def convert_to_hrs(string)
if !string.nil?
string.strip!
case string.count(':')
when 0
'00:00:' + string.rjust(2, '0')
when 1
'00:' + string
else
string
end
else
"00:00:00"
end
end
def duration_in_milliseconds(input)
input = convert_to_hrs(input)
if input.match(/\d+:\d\d:\d\d\.?\d*/)
h, m, s = input.split(':').map(&:to_i)
(h.hours + m.minutes + s.seconds) * 1000
else
0
end
end
Unfortunately, when I call duration_in_milliseconds(input) with a number like 8:49, the result is zero. The result should be interpreted as 8 minutes and 49 seconds, which in milliseconds would be 529000. How do I adjust the above to account for this?
I'll just leave this one here:
def convert_to_ms(string)
string.split(':').map(&:to_i).inject(0) { |a, b| a * 60 + b } * 1000
end
convert_to_ms('8:49')
#=> 529000
Just another implementation.. Cause it's fun :)
def duration_in_milliseconds(string)
string.split(':')
.map(&:to_i)
.reverse
.zip([1,60,3600])
.map{ |segment, multiplier| segment*multiplier }
.inject(:+) * 1000
end

Any option to post-process returned value in long conditional, other than setting variables for each statement?

def some_method(x)
if x == 1
date = Date.today
elsif x == 5
date = Date.today + 2
else
date = Date.today - 2
end
date + 20
end
For visual clarity, is it possible somehow to omit date = for each statement and catch whatever the returned value is from the conditional and add 20 to it?
(The code is for example purpose, my own code has 10 if-statements.)
def some_method(x)
date = if x == 1
Date.today
elsif x == 5
Date.today + 2
else
Date.today - 2
end
date + 20
end
If you have 10 if statements it is probably better to refactor code using case-when like this:
def some_method(x)
date = case x
when 1; Date.today
when 5; Date.today + 2
else; Date.today - 2
end
date + 20
end

Moving logic from controller to model in rails 3?

I've been building a contest application, and I can easily tell I've been putting wayyyy too much logic in the controller. How can I go about switch this type of logic to the model? (whats important here isn't the logic itself - its far from finished - I'm just trying to understand how to move it out of the controller).
Controller:
def create
#person = Person.new(params[:person])
#yournum = rand(100)
#day = Day.find_by_id(1)
#prereg = Prereg.find_by_email(#person.email)
if #preg != nil
#person.last_name = #prereg.name
end
if #day.number == 1
if #yournum <= 25
#person.prize_id = 2
elsif #yournum > 25 && #yournum <=50
#person.prize_id = 1
elsif #yournum > 51 && #yournum <=75
#person.prize_id = 3
elsif #yournum > 76 && #yournum <=100
#person.prize_id = 4
end
elsif #day.number == 2
if #yournum <= 25
#person.prize_id = 2
elsif #yournum > 25 && #yournum <=50
#person.prize_id = 1
elsif #yournum > 51 && #yournum <=75
#person.prize_id = 3
elsif #yournum > 76 && #yournum <=100
#person.prize_id = 4
end
elsif #day.number == 3
if #yournum <= 50
#person.prize_id = 2
elsif #yournum > 51 && #yournum <=90
#person.prize_id = 1
elsif #yournum > 91 && #yournum <= 95
#person.prize_id = 3
elsif #yournum > 96 && #yournum <=100
#person.prize_id = 4
end
end
#person.save
redirect_to #person
end
Model:
class Person < ActiveRecord::Base
belongs_to :prize
end
Thanks!
Elliot
Indeed, that's a pretty ugly controller. As you say, the solution is easy: move all the logic to the model:
def create
#person = Person.new(params[:person])
#person.set_price
if #person.save
redirect_to #person
else
flash[:error] = ...
render :action => 'new'
end
end
class Person
def set_price
# your logic here
end
end
Note that:
Controller: you need to check if #person was actually saved (maybe some validation failed).
Model: If a person has always to be assigned a price on creation, then use a callback (before_validation, for example). Otherwise, call it from the controller as shown the code above.
class PersonsController < ApplicationController
respond_to :html
def create
#person = Person.new(params[:person])
if #person.save
respond_with #person
else
flash[:error] = 'Render error'
render :action => :new
end
end
end
class Person
before_create :method_name
def method_name
#Put whatever you want to happen before creation here
end
end

Resources