RUBY - Currency Exchange Rate Calculator - ruby

I'm trying to create a currency converter in Ruby which will calculate the exchange rate between two currencies on a given date.
I have a data file containing test data (date, currency from, currency to). The test data is in EUR, so all rates are converted to EUR and then to the target currency.
So far I have 3 files (Exchange.rb, Test_Exchange.rb, rates.json):
Exchange.rb:
require 'json'
require 'date'
module Exchange
# Return the exchange rate between from_currency and to_currency on date as a float.
# Raises an exception if unable to calculate requested rate.
# Raises an exception if there is no rate for the date provided.
#rates = JSON.parse(File.read('rates.json'))
def self.rate(date, from_currency, to_currency)
# TODO: calculate and return rate
rates = u/rates[date] # get rates of given day
from_to_eur = 1.0 / rates[from_currency] # convert to EUR
from_to_eur * rates[to_currency] # convert to target currency
end
end
Test_Exchange.rb:
require_relative 'Exchange.rb'
require 'date'
target_date = Date.new(2018,12,10).to_s
puts "USD to GBP: #{Exchange.rate(target_date, 'USD', 'GBP')}"
puts "USD to JPY: #{Exchange.rate(target_date, 'PLN', 'CHF')}"
puts "DKK to CAD: #{Exchange.rate(target_date, 'PLN', 'CHF')}"
rates.json:
{
"2018-12-11": {
"USD": 1.1379,
"JPY": 128.75,
"BGN": 1.9558,
"CZK": 25.845,
"DKK": 7.4641,
"GBP": 0.90228,
"HUF": 323.4,
"CHF": 1.1248,
"PLN": 4.2983
},
"2018-12-10": {
"USD": 1.1425,
"JPY": 128.79,
"BGN": 1.9558,
"CZK": 25.866,
"DKK": 7.4639,
"CAD": 1.5218,
"GBP": 0.90245,
"HUF": 323.15,
"PLN": 4.2921,
"CHF": 1.1295,
"ISK": 140.0,
"HRK": 7.387,
"RUB": 75.8985
},
"2018-12-05": {
"USD": 1.1354,
"JPY": 128.31,
"BGN": 1.9558,
"CZK": 25.886,
"DKK": 7.463,
"GBP": 0.88885,
"HUF": 323.49,
"PLN": 4.2826,
"RON": 4.6528,
"SEK": 10.1753,
"CHF": 1.1328,
"HRK": 7.399,
"RUB": 75.8385,
"CAD": 1.5076
}
}
I'm not sure what to add in the Exchange.rb file to allow the user to input a date and the two currencies to compare exchange rates.
Running Exchange.rb does nothing. I'm guessing it wants a date and currency parameters input?
Running Test_Exchange.rb works because the date and currencies are bootstrapped in.
I found almost the same question posted here a couple years ago, but the thread is now closed, and the solution was incomplete. Hoping someone can help me!
RUNNING EDIT:
Exchange.rb:
require 'json'
require 'date'
module Exchange
# Return the exchange rate between from_currency and to_currency on date as a float.
# Raises an exception if unable to calculate requested rate.
# Raises an exception if there is no rate for the date provided.
#rates = JSON.parse(File.read('rates.json'))
#Grab Date and Currencies from User
puts "Please enter a Date (YYYY-MM-DD)"
input_date = gets.chomp
puts "The Date you entered is: #{input_date}"
puts "Please enter a 3-letter Currency Code (ABC):"
input_curr_1 = gets.chomp
puts "The 1st Currency you entered is: #{input_curr_1}"
puts "Please enter a 2nd 3-letter Currency Code (XYZ):"
input_curr_2 = gets.chomp
puts "The 2nd Currency you entered is: #{input_curr_2}"
def self.rate(input_date, input_curr_1, input_curr_2)
# TODO: calculate and return rate
rates = #rates[input_date] # get rates of given day
from_to_eur = 1.0 / rates[input_curr_1] # convert to EUR
from_to_eur * rates[input_curr_2] # convert to target currency
end
end
I think I have to use a put and a get to capture the user date input? Then the same for each of the two currencies? Of course, my syntax for the date stuff is all wrong..
So I managed to use the gets and put functions. Now all that's left is somehow calling the rates.json file and comparing the user inputs to the existing data..

Related

DateTime Ruby How to format correctly

My project is supposed to fetch specific values from multiple hashes a put those values in a text file. Ideally what I need my code to do is to have every date for the employees be seven days apart, so the text file would look something like this:
"Rachel Thorndike
2017-10-09-T04:29:46-05:00
Stacie Smith
2017-10-16-T04:29:46-05:00"
What this is supposed to do is fetch employee's names and put the time of their "handoff" on the line under them. I looked online and found the DateTime that Ruby features but it looks like whatever I do isn't working. My code is this:
require 'date'
jsonUser["users"].each do |user|
somefile.puts user["user"]["summary"]
print 'Handoff Date + Time: '
parsed = DateTime.strptime(jsonUser["start"], '%d-%m-%Y %H:%M')
utc = parsed.next_day(7).strftime('%d-%m-%Y %H:%M')
puts utc
end
But terminal returns this code with an error 'strptime': invalid date (ArgumentError). Would anybody help me get this code to work the way I want to? Anything that points me to the right direction? With explanations, if it isn't too much.
Thank you so much!
Update
I was able to get the iso8601 to appear under their name. My new code is
require 'date'
jsonUser["users"].each do |user|
somefile.puts user["user"]["summary"]
print 'Handoff Date + Time: '
parsed = DateTime.iso8601(jsonUser["start"])
utc = parsed.next_day(7).iso8601
somefile.puts utc
end
BUT the .next_day method isn't increasing by 7 days that I want too. Thought? I have only got one value that is appearing and that is going under every line. Its the value of jsonUser["start"] + 7 days...so `2017-
10-16T04:29:46-05:00`
This is what parse.next_day(7) gives me.
"Sr Chid
Handoff Date + Time: 2017-10-16T04:29:46-05:00
Ash A
Handoff Date + Time: 2017-10-16T04:29:46-05:00
Ven D
Handoff Date + Time: 2017-10-16T04:29:46-05:00
Abhi S
Handoff Date + Time: 2017-10-16T04:29:46-05:00"
The value of jsonUser["start"] is 2017-10-09T04:29:46-05:00 so the good thing is that it did increase by 7 but it only did it once.
Update for Amadan
require 'date'
date = DateTime.iso8601(jsonUser["start"])
jsonUser["users"].each do |user|
if user["user"]["self"] == nil
nil
else
somefile.puts user["user"]["summary"].gsub(/\w+/, &:capitalize).gsub(/[.]/, ' ')
somefile.print 'Handoff Date + Time: '
date = date.next_day(7)
somefile.puts date.iso8061
end
end

How calculate the number prorated day with Stripe API?

i am using Stripe. I would like to know how can calculate number of day prorated
I want display something like that
1 additional seat ($9/month each - prorated for 26 days)
in the api i don't see any item prorate_day
Bolo
subscription_proration_date what you are looking for? Then it will calculate it for you.
See more at https://stripe.com/docs/subscriptions/guide
The example of pro-rated subscription in ruby is as follows
# Set your secret key: remember to change this to your live secret key in production
# See your keys here https://dashboard.stripe.com/account/apikeys
Stripe.api_key = "sk_test_9OkpsFpKa1HDHaZa7e0BeGaO"
proration_date = Time.now.to_i
invoice = Stripe::Invoice.upcoming(:customer => "cus_3R1W8PG2DmsmM9", :subscription => "sub_3R3PlB2YlJe84a",
:subscription_plan => "premium_monthly", :subscription_proration_date => proration_date)
current_prorations = invoice.lines.data.select { |ii| ii.period.start == proration_date }
cost = 0
current_prorations.each do |p|
cost += p.amount
end
# Display the cost of these prorations invoice items to the end user,
# and actually do the update when they agree.
# To make sure that the proration is calculated the same as when it was previewed,
# you need to pass in the proration_date parameter
# later...
subscription = Stripe::Subscription.retrieve("sub_3R3PlB2YlJe84a")
subscription.plan = "premium_monthly"
subscription.proration_date = proration_date
subscription.save

How to avoid cyclometic complexity caused by guard blocks?

I have the following function in one of my classes
def intraday_time_series(opts)
### Guard block ###
valid_resource = opts[:resource] && [:calories, :steps, :distance, :floors, :elevation].include?(opts[:resource])
valid_date = opts[:date]
valid_detail_level = opts[:detailLevel] && %w(1min 15min).include?(opts[:detailLevel])
raise FitgemOauth2::InvalidArgumentError,
'Must specify resource to fetch intraday time series data for.'\
' One of (:calories, :steps, :distance, :floors, or :elevation) is required.' unless valid_resource
raise FitgemOauth2::InvalidArgumentError, 'Must specify the date to fetch intraday time series data for.' unless valid_date
raise FitgemOauth2::InvalidArgumentError,
'Must specify the data resolution to fetch intraday time series data for.'\
' One of (\"1d\" or \"15min\") is required.' unless valid_detail_level
### actual logic ###
resource = opts.delete(:resource)
date = format_date(opts.delete(:date))
detail_level = opts.delete(:detailLevel)
time_window_specified = opts[:startTime] || opts[:endTime]
resource_path = "user/#{#user_id}/activities/"
if time_window_specified
start_time = format_time(opts.delete(:startTime))
end_time = format_time(opts.delete(:endTime))
resource_path += "#{resource}/date/#{date}/1d/#{detail_level}/time/#{start_time}/#{end_time}.json"
else
resource_path += "#{resource}/date/#{date}/1d/#{detail_level}.json"
end
get_call(resource_path)
end
The function has a guard block that checks for any issues with the arguments (the guard block) and if no error is found it does stuff with those arguments.
Now, when I use rubocop to analyze my code, it reports high cyclometic complexity due to the guard blocks. One way I can reduce the complexity is to define another function guard_for_intraday_time_series and move all the guard blocks there. I do not think it as an appropriate solution though because it will populate my code with a guard block for every function that I have in my project.
What is an appropriate way to reduce this complexity, or is it just unavoidable?
You have many intermediate local variables that make the code complicated.
I would refactor it like this:
def intraday_time_series(resource: nil, date: nil,
detailLevel: nil, startTime: nil, endTime: nil)
unless %i[calories steps distance floors elevation].include?(resource)
raise FitgemOauth2::InvalidArgumentError,
"Must specify resource to fetch intraday time series data for."\
" One of (:calories, :steps, :distance, :floors, or :elevation) is required."
end
unless date
raise FitgemOauth2::InvalidArgumentError,
"Must specify the date to fetch intraday time series data for."
end
unless %w(1min 15min).include?(detailLevel)
raise FitgemOauth2::InvalidArgumentError,
"Must specify the data resolution to fetch intraday time series data for."\
" One of (\"1d\" or \"15min\") is required."
end
resource_path = [
"user", #user_id,
"activities", resource,
"date", format_date(date),
"1d", detailLevel
].join("/")
if startTime || endTime
resource_path =
[resource_path, "time", format_time(startTime), format_time(endTime)].join("/")
end
get_call("#{resource_path}.json")
end

I want to create a new local variable from the sum of two others in Ruby but I'm stuck

I am trying to change one example to take a user input rather than using hard coded values then use those local variables to work out items needed.
So far my code looks like this:
print "Number of cars available today."
cars = gets.chomp()
print "Number of available seats in the car."
space_in_a_car = gets.chomp()
print "Number of drivers available."
drivers = gets.chomp()
print "Number of passagers that need transport."
passangers = gets.chomp
cars_not_driven = #{cars} - #{drivers}
cars_driven = drivers
carpool_capacity = #{cars_driven} * #{space_in_a_car}
average_passanger_per_car = #{passangers} / #{drivers}
print "The number of cars being driven today is #{cars_driven}.\n"
print "The number of cars not being driven today is #{cars_not_driven}.\n"
print "We have #{carpool_capacity} cars available.\n"
print "So we need to carry #{average_passanger_per_car} passangers per car to make sure we can transport everyone.\n"
The code will run without throwing any errors but of course because I am not getting the correct commands in:
cars_not_driven = #{cars} - #{drivers}
cars_driven = drivers
carpool_capacity = #{cars_driven} * #{space_in_a_car}
average_passanger_per_car = #{passangers} / #{drivers}
the only value I am getting in the return is:
print "The number of cars being driven today is #{cars_driven}.\n"
How should I be writing:
cars_not_driven = #{cars} - #{drivers} etc
to get the number of cars_not_driven?
I don't understand very well why you use this #{var} and <br>. If I am right that you want to use plain ruby, this should be the solution:
print "Number of cars available today."
cars = gets.chomp().to_i
print "Number of available seats in the car."
space_in_a_car = gets.chomp().to_i
print "Number of drivers available."
drivers = gets.chomp().to_i
print "Number of passagers that need transport."
passangers = gets.chomp.to_i
cars_not_driven = cars - drivers
cars_driven = drivers
carpool_capacity = cars_driven * space_in_a_car
average_passanger_per_car = passangers / drivers
print "The number of cars being driven today is #{cars_driven}.\n"
print "The number of cars not being driven today is #{cars_not_driven}.\n"
print "We have #{carpool_capacity} cars available.\n"
print "So we need to carry #{average_passanger_per_car} passangers per car to make sure we can transport everyone.\n"
When you use gets Ruby is expecting and returning a String. So variables car, drivers etc are all String.
In order to do integer operation over them, you need to convert them to integers. In Ruby you can do this using .to_i.
Now with that information, try:
cars_not_driven = cars.to_i - drivers.to_i
cars_driven = drivers.to_i
carpool_capacity = cars_driven * space_in_a_car.to_i
average_passanger_per_car = passangers.to_i / drivers.to_i
You can check the class of a variable using .class. Here:
cars = gets.chomp()
10
# => "10"
cars.class
# => String
drivers = gets.chomp()
20
# => "20"
drivers.class
# => String
Now lets add them:
cars + drivers
# => "1020"
Since they are string, + operator is adding two strings to one. Not something you intended. Now try this:
cars.to_i + drivers.to_i
# => 30

Building Data Structure out of JSON output

Hi Im working on writing a script that pulls data out of a ticketing system. Once it pulls the data it analyze the content of it and if it the content match a specific criteria then it needs to build a data structure file that will be dump in the same server.
I was able to parse the data in JSON format, listed below is the content:
[{"id"=>10423,
"type"=>"Ticket",
"lastUpdated"=>"2014-11-04T10:58:47Z",
"shortSubject"=>"FOO STATUS UPDATE",
"shortDetail"=>"Reply to this message if all systems are functional..",
"displayClient"=>"No Client",
"updateFlagType"=>0,
"prettyLastUpdated"=>"54 minutes ago",
"latestNote"=>
{"id"=>16850,
"type"=>"TechNote",
"mobileListText"=>"<b>t. trust: </b> All Systems are OK",
"noteColor"=>"clear",
"noteClass"=>"bubble right"}},
{"id"=>10422,
"type"=>"Ticket",
"lastUpdated"=>"2014-11-04T10:54:07Z",
"shortSubject"=>"FOO STATUS UPDATE",
"shortDetail"=>"Reply to this message if all systems are functional..",
"displayClient"=>"No Client",
"updateFlagType"=>0,
"prettyLastUpdated"=>"58 minutes ago",
"latestNote"=>nil},
{"id"=>10421,
"type"=>"Ticket",
"lastUpdated"=>"2014-11-04T10:53:17Z",
"shortSubject"=>"FOO STATUS UPDATE",
"shortDetail"=>"Reply to this message if all systems are functional..",
"displayClient"=>"No Client",
"updateFlagType"=>0,
"prettyLastUpdated"=>"59 minutes ago",
"latestNote"=>nil}]
In the data above you can see that each ticket has an id, lastupdate, short Subject, short Detail and lastest note the value of the latest note will be nill if no one reply to the ticket but if someone does reply then the value mobileListText will have something.
So what I need to do pretty much is once I get this data the script will look for the subject that complies with "FOO STATUS UPDATE" if that value matches then looks for the content of the shortDetail matches "Reply to this message if all systems are functional.." and if this complies then looks for its latestNote, if latestNote is nill then it will create a log file specifiying date and time when it run, the id of the ticket with this state and a message saying, ticket has not being reply, but if the latest note has the value "mobileListText"=>"t. trust: All Systems are OK", then creates the follwing data structure:
{"LastUpdate":1415130257,"Service":[{"time":"11-04-2014 10:58:47 GMT","region:":"","id":"","description":"All Systems are OK","service":""},{"time":"11-04-2014 10:54:07 GMT","region:":"","id":"","description":"All Systems are OK","service":""},{"time":"11-04-2014 10:53:17 GMT","region:":"","id":"","description":"All Systems are OK","service":""}]}
Im able to have part of this however, based on the data above, only one ticket has All Systems are OK, meaining that only one of the tickets has being reply, and it only should write something like this:
{"LastUpdate":1415130257,"Service":[{"time":"11-04-2014 10:58:47 GMT","region:":"","id":"","description":"All Systems are OK","service":""}]}
But Instead repeats this only ticket that has being replied serveral times.
this my code so far:
require 'rubygems'
require 'json'
require 'net/http'
require 'highline/import'
require 'pp'
require 'logger'
#usersol='foo'
#passol= 'foo123'
#urlsol= "http://dev-webhelpdesk.foo.corp:8081/helpdesk/WebObjects/Helpdesk.woa/ra/Tickets?list=group&page=1&limit=#{#limit}&username=#{#usersol}&password=#{#passol}"
#limit = '25'
#log = #log= Logger.new( 'message_solar.log')
def ticket_data #looks for ticket data in solarwinds
resp = Net::HTTP.get_response(URI.parse(#urlsol))
url_output = resp.body
JSON.parse(url_output)
end
#CRONJOB THAT START ALL
#echo "Reply to this message if all systems are functional.." | mail -r noc#foo.com -s "FOO STATUS UPDATE:" noc-team#FOO.com >> /dev/null
# Looking for all the tickets with the following content
# ticket id, ticket subject and content
def search_allok(allok)
description = []
allok.each do |systems|
output1 = systems.has_key?'id'
if output1
systems.values_at('shortSubject').each do | subject |
output2 = subject.match(%r(TRUST STATUS UPDATE))
if output2
latestnote = systems.values_at('latestNote')
latestnote.each do |content|
if content
final = content.values_at('mobileListText')
final_ok = final[0].sub!(/^\<b\>.*\<\/b\>\s/, "")
systems_ok = final_ok.match(%r(All Systems are OK))
if systems_ok
ids = systems['id']
notify = {"LastUpdate" => Time.now.to_i, "Service" => []}
allok.each do |lastup|
reference = lastup.has_key? 'id'
if reference
timeid = lastup.values_at('lastUpdated')
timeid.each do |lines|
final=lines.split(/[-, T, Z]/)
notify["Service"] << { "time" => "#{final[1]}-#{final[2]}-#{final[0]} #{final[3]} GMT", "region:" => "", "id" => "#{ids}", "description" => "#{systems_ok}" , "service" => ''}
end
end
end
File.open("notify.json", "w") do |fileformatted|
fileformatted.puts (JSON.dump(notify))
end
else
time = Time.now
#log.info("#{time} - Ticket ID #{systems['id']} has not being updated")
end
else
#log.info("#{time} - Ticket ID #{systems['id']} has not being reply")
end
end
end
end
end
end
end
# If the content is there then it need to create
# the data structure including the lastupdated
# (time when it run the script), and the lastupdate for the ticket
# and the description All Systems OK
#This method below I added to the one above, but I was thinking on doing it separate but I encouter issues passing the information needed from above to below
def datastructure(format_file) #creates JSON file lastupdated of each ticket in the queue
notify = {"LastUpdate" => Time.now.to_i, "Service" => []}
format_file.each do |lastup|
reference = lastup.has_key? 'id'
if reference
timeid = lastup.values_at('lastUpdated')
timeid.each do |lines|
final=lines.split(/[-, T, Z]/)
notify["Service"] << { "time" => "#{final[1]}-#{final[2]}-#{final[0]} #{final[3]} GMT", "region:" => "", "id" => "", "description" => region , "service" => ''}
end
end
end
File.open("notify.json", "w") do |fileformatted|
fileformatted.puts (JSON.dump(notify))
end
end
#ticket_data
#datastructure(ticket_data)
search_allok(ticket_data)
The code you've written is basically doing a roundabout version of what is achieved by using ruby's map and select methods. See this article: Ruby Explained: Map, Select, and Other Enumerable Methods

Resources