Rails survey calculating results and storing results in DB - ruby-on-rails

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).

Related

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

rails assign attributes to instance

I am trying to calculate how much the total cost of my transaction will be. And to test I am running Transaction.last.calculate_total_payment, it is returning a BigDecimal which is what I want but none of the attributes are being updated on the instance after. What am I doing wrong?
class Transaction < ActiveRecord::Base
has_one :auction
has_one :bid
belongs_to :inventory_part
TIER0 = 0
TIER1 = 5_000
TIER2 = 50_000
TIER3 = 500_000
TIER4 = 1_000_000
.
.
.
.
def calculate_total_payment
part = self.bid.part_price
self.tax = part * self.tax_rate
if self.bid.tx.shipping_account
self.final_shipping_cost = 0
else # testing purposes
self.final_shipping_cost = self.bid.est_shipping_cost
end
price_before_fees = part + self.tax + self.final_shipping_cost
if price_before_fees < TIER1 #5,000
self.bid_aero_fee = price_before_fees * 0.025
self.armor_fee = price_before_fees * 0.015
elsif price_before_fees < TIER2 #50,000
self.bid_aero_fee = (price_before_fees - TIER1) * 0.015 + 125
self.armor_fee = (price_before_fees - TIER1) * 0.01 + 75
elsif price_before_fees < TIER3 #500,000
self.bid_aero_fee = (price_before_fees - TIER2) * 0.0125 + 800
self.armor_fee = (price_before_fees - TIER2) * 0.0075 + 525
elsif price_before_fees < TIER4 #1,000,000
self.bid_aero_fee = (price_before_fees - TIER3) * 0.0075 + 6425
self.armor_fee = (price_before_fees - TIER3) * 0.005 + 3900
else # anything over a million
self.bid_aero_fee = (price_before_fees - TIER4) * 0.0075 + 10175
self.armor_fee = (price_before_fees - TIER4) * 0.0035 + 6400
end
self.total_fee = self.armor_fee + self.bid_aero_fee
self.total_amount = price_before_fees + self.total_fee
end
end
You are never calling save in the calculate_total_payment method. You need to explicitly save the object for it to be persisted in the DB.
You should call save! at the end if you want to make your changes persistent in the database. Without this, it will modify the object in place (so changes will disappear after reloading of fetching the object again).

self.method (params) Rails

I'm newbie and I would like to know if is it possible to define model methods with params. I mean. I have this method to calculate distances using spherical coordinates
#in my model
#Haversin formula to calculate distance between spherical coordinates
def self.distance(b)
rad_per_deg = Math::PI/180 # PI / 180
rkm = 6371 # Earth radius in kilometers
#rm = rkm * 1000 # Radius in meters
a=[]
a.push(self.lat)
a.spuh(self.long)
dlon_rad = (b[1]-a[1]) * rad_per_deg # Delta, converted to rad
dlat_rad = (b[0]-a[0]) * rad_per_deg
lat1_rad, lon1_rad = a.map! {|i| i * rad_per_deg }
lat2_rad, lon2_rad = b.map! {|i| i * rad_per_deg }
a = Math.sin(dlat_rad/2)**2 + Math.cos(lat1_rad) * Math.cos(lat2_rad) * Math.sin(dlon_rad/2)**2
c = 2 * Math.asin(Math.sqrt(a))
distance=rkm * c
return distance
end
I want this to work like: obj.distance(b) where b is an array of latitude and longitude. But when I try this on irb I get:
NoMethodError: undefined method `distance' for #<Object:0x000000058854c8>
Probably I'm missing something.
class Meteo < ActiveRecord::Base
attr_accessible :date, :humidity, :lat, :long, :pressure, :temp, :town, :wind, :wind_direction
, :rain_quantity
#Haversin formula to calculate distance between spheric coordinates
def self.distance(b)
rad_per_deg = Math::PI/180 # PI / 180
rkm = 6371 # Earth radius in kilometers
#rm = rkm * 1000 # Radius in meters
a=[]
a.push(self.lat)
a.spuh(self.long)
dlon_rad = (b[1]-a[1]) * rad_per_deg # Delta, converted to rad
dlat_rad = (b[0]-a[0]) * rad_per_deg
lat1_rad, lon1_rad = a.map! {|i| i * rad_per_deg }
lat2_rad, lon2_rad = b.map! {|i| i * rad_per_deg }
a = Math.sin(dlat_rad/2)**2 + Math.cos(lat1_rad) * Math.cos(lat2_rad) * Math.sin(dlon_rad/2)
**2
c = 2 * Math.asin(Math.sqrt(a))
distance=rkm * c
return distance
end
end
And I call this on irb like:
irb> m=Meteo.last
irb> b=[86.43971008189519, 23.477053751481986]
irb> m.distance(b)
Just remove self.
When you write def self.distance, you mean that the method will be called on the model class. You should use def distance if you want the method to be called on a model instance.
Compare:
class SomeModel
def self.distance
# ...
end
end
SomeModel.distance
With:
class SomeModel
def distance
# ...
end
end
obj = SomeModel.new
obj.distance
If I get you right, you are defining a class method (with def self.distance) but are calling it on an instance of that class (obj.distance(array)).
You should call the method on obj's class e.g. Meteo.distance(array).
Or simply define it as an instance method, by just leaving the self in method definition.
Hope, that helps

Rails - Simple Loop Not Working

In my controller I am trying to do a bulk insert into a table, in my first attempt it works but the names somehow get mangled as the following: (loop runs 24 times which is what I want)
test-port-name-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21
test-port-name-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20
test-port-name-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19
test-port-name-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18
test-port-name-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17
test-port-name-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16
test-port-name-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15
test-port-name-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14
test-port-name-0-1-2-3-4-5-6-7-8-9-10-11-12-13
test-port-name-0-1-2-3-4-5-6-7-8-9-10-11-12
test-port-name-0-1-2-3-4-5-6-7-8-9-10-11
test-port-name-0-1-2-3-4-5-6-7-8-9-10
test-port-name-0-1-2-3-4-5-6-7-8-9
test-port-name-0-1-2-3-4-5-6-7-8
test-port-name-0-1-2-3-4-5-6
test-port-name-0-1-2-3-4-5-6-7
test-port-name-0-1-2-3-4-5
test-port-name-0-1-2-3-4
test-port-name-0-1-2
test-port-name-0-1-2-3
test-port-name-0
test-port-name-0-1
test-port-name-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22
test-port-name-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23
instead of test-port-name-0 .... test-port-name-23
def bulk_port_import
if request.post?
#attempt create
count = 0
for i in 1..session[:no_ports]
params[:dp][:name] = params[:dp][:name] + '-' + count.to_s
#dp = DevicePort.create params[:dp]
count = count + 1
end
end
#success = "Saved." if #dp.valid?
#error = ""
#dp.errors.each_full {|e| #error += e + ", "}
redirect_to '/device/update/' + params[:dp][:device_id]
end
Different attempt:
def bulk_port_import
if request.post?
#attempt create
i = 0
while i < session[:no_ports] do
params[:dp][:name] = params[:dp][:name] + '-' + i.to_s
#dp = DevicePort.create params[:dp]
i++
end
end
session.delete(:no_ports)
#success = "Saved." if #dp.valid?
#error = ""
#dp.errors.each_full {|e| #error += e + ", "}
redirect_to '/device/update/' + params[:dp][:device_id]
end
but with this I get syntax error, unexpected kEND and I can't see what I'm doing wrong in either case, it's probably something stupid, again.
Its because you are changing params[:dp][:name] in the loop
def bulk_port_import
if request.post?
#attempt create
count = 0
for i in 1..session[:no_ports]
dp_name = params[:dp][:name] + '-' + count.to_s
#dp = DevicePort.create(params[:dp].merge(:name => dp_name))
count = count + 1
end
end
#success = "Saved." if #dp.valid?
#error = ""
#dp.errors.each_full {|e| #error += e + ", "}
redirect_to '/device/update/' + params[:dp][:device_id]
end

Paypal Setexpresscheckout ItemDescription Grails

I can't set the item summary in set express checkoput. L_NAME0=A caused error
def strUsername = "***"
def strPassword = "***"
def strSignature = "***"
def strCredentials = "USER=" + strUsername + "&PWD=" + strPassword + "&SIGNATURE=" + strSignature
def strNVPSandboxServer = "https://api-3t.sandbox.paypal.com/nvp";
def user = session.userId +","+amt + "," + receiver + "," + address + "," + opt
def successUrl = '***
def cancelUrl = '***'
def strAPIVersion = "56.0"
def strNVP = strCredentials + "&METHOD=SetExpressCheckout&AMT=" + totalamount +"&PAYMENTACTION=Sale&RETURNURL="+ successUrl+"&CANCELURL="+ cancelUrl +"&CURRENCYCODE=SGD&ITEMAMT="+totalamount+"&L_NAME0=OHN&VERSION=" + strAPIVersion
Try updating def strAPIVersion = "56.0" to def strAPIVersion = "76.0" I think the advanced features a not available in the 56 version. I used the default set by Paypal as a test
&L_PAYMENTREQUEST_0_NAME0=10% Decaf Kona Blend Coffee
&L_PAYMENTREQUEST_0_NUMBER0=623083
&L_PAYMENTREQUEST_0_DESC0=Size: 8.8-oz
&L_PAYMENTREQUEST_0_AMT0=9.95
&L_PAYMENTREQUEST_0_QTY0=2
&L_PAYMENTREQUEST_0_NAME1=Coffee Filter bags
&L_PAYMENTREQUEST_0_NUMBER1=623084
&L_PAYMENTREQUEST_0_DESC1=Size: Two 24-piece boxes
&L_PAYMENTREQUEST_0_AMT1=39.70
&L_PAYMENTREQUEST_0_QTY1=2
&PAYMENTREQUEST_0_ITEMAMT=99.30
&PAYMENTREQUEST_0_TAXAMT=2.58
&PAYMENTREQUEST_0_SHIPPINGAMT=3.00
&PAYMENTREQUEST_0_HANDLINGAMT=2.99
&PAYMENTREQUEST_0_SHIPDISCAMT=-3.00
&PAYMENTREQUEST_0_INSURANCEAMT=1.00
&PAYMENTREQUEST_0_AMT=105.87
&ALLOWNOTE=1

Resources