How to retrieve entire cost for a SoftLayer machine, including any extra costs such as bandwidth overages? - ruby

I've been retrieving monthly invoice cost information on our SoftLayer accounts for quite some time using the Ruby softlayer gem. However, there is a concern in the team that we may be missing certain costs, such as any overages on network utilization. I'd like to have some piece of mind that what I'm doing is correctly gathering all costs and we are not missing anything. Here is my code/query:
account = SoftLayer::Service.new("SoftLayer_Account",:username => user, :api_key => api_key, :timeout => 999999999)
softlayer_client = SoftLayer::Client.new(:username => user, :api_key => api_key, :timeout => 999999999)
billing_invoice_service = softlayer_client.service_named("Billing_Invoice")
object_filter = SoftLayer::ObjectFilter.new
object_filter.set_criteria_for_key_path('invoices.createDate', 'operation' => 'betweenDate', 'options' => [{'name' => 'startDate', 'value' => ["#{startTime}"]}, {'name' => 'endDate', 'value' => ["#{endTime}"]}])
# Set startDate and endDate around the beginning of the month in search of the "Recurring" invoice that should appear on the 1st.
invoices = account.result_limit(0,10000).object_filter(object_filter).object_mask("mask[id,typeCode,itemCount,invoiceTotalAmount,closedDate,createDate]").getInvoices
invoices.each do | invoice |
if invoice["typeCode"] == "RECURRING"
invoice_reference = billing_invoice_service.object_with_id(invoice["id"])
invoice_object = invoice_reference.object_mask("mask[itemCount]").getObject
billing_items_count = invoice_object["itemCount"]
billing_machines_map = Hash.new
all_billing_items = Array.new
# Search for billing items containing a hostName value.
# The corresponding billing item ID will become the key of a new hash.
# Child costs will be added to the existing costs.
billing_items_retrieval_operation = proc {
for i in 0..(billing_items_count/8000.0).ceil - 1
billing_items = invoice_reference.result_limit(i*8000, 8000).object_mask("mask[id,resourceTableId,billingItemId,parentId,categoryCode,hostName,domainName,hourlyRecurringFee,laborFee,oneTimeFee,recurringFee,recurringTaxAmount,setupFee,setupTaxAmount,location[name]]").getItems()
billing_items.each do | billing_item |
if billing_item["hostName"]
billing_machines_map[billing_item["id"]] = billing_item
end
end
all_billing_items.concat(billing_items)
end
}
# Look for items with parentIds or resourceTableIds.
# Both Ids represent a "parent" of the item.
# Give higher importance to parentId.
billing_items_retrieval_callback = proc {
cost_of_billing_items_without_parent = BigDecimal.new("0.00")
all_billing_items.each do | billing_item |
if billing_item["parentId"] != ""
parent_billing_machine = billing_machines_map[billing_item["parentId"]]
if parent_billing_machine parent_billing_machine["recurringFee"] = (BigDecimal.new(parent_billing_machine["recurringFee"]) + BigDecimal.new(billing_item["recurringFee"])).to_s('F')
parent_billing_machine["setupFee"] = (BigDecimal.new(parent_billing_machine["setupFee"]) + BigDecimal.new(billing_item["setupFee"])).to_s('F')
parent_billing_machine["laborFee"] = (BigDecimal.new(parent_billing_machine["laborFee"]) + BigDecimal.new(billing_item["laborFee"])).to_s('F')
parent_billing_machine["oneTimeFee"] = (BigDecimal.new(parent_billing_machine["oneTimeFee"]) + BigDecimal.new(billing_item["oneTimeFee"])).to_s('F')
end
elsif billing_item["resourceTableId"] != ""
parent_billing_machine = billing_machines_map[billing_item["resourceTableId"]]
if parent_billing_machine
parent_billing_machine["recurringFee"] = (BigDecimal.new(parent_billing_machine["recurringFee"]) + BigDecimal.new(billing_item["recurringFee"])).to_s('F')
parent_billing_machine["setupFee"] = (BigDecimal.new(parent_billing_machine["setupFee"]) + BigDecimal.new(billing_item["setupFee"])).to_s('F')
parent_billing_machine["laborFee"] = (BigDecimal.new(parent_billing_machine["laborFee"]) + BigDecimal.new(billing_item["laborFee"])).to_s('F')
parent_billing_machine["oneTimeFee"] = (BigDecimal.new(parent_billing_machine["oneTimeFee"]) + BigDecimal.new(billing_item["oneTimeFee"])).to_s('F')
end
else
cost_of_billing_items_without_parent = (BigDecimal.new(cost_of_billing_items_without_parent) + BigDecimal.new(billing_item["recurringFee"])).to_s('F')
cost_of_billing_items_without_parent = (BigDecimal.new(cost_of_billing_items_without_parent) + BigDecimal.new(billing_item["setupFee"])).to_s('F')
cost_of_billing_items_without_parent = (BigDecimal.new(cost_of_billing_items_without_parent) + BigDecimal.new(billing_item["laborFee"])).to_s('F')
cost_of_billing_items_without_parent = (BigDecimal.new(cost_of_billing_items_without_parent) + BigDecimal.new(billing_item["oneTimeFee"])).to_s('F')
end
end
pp "INVOICE: Total cost of devices for account without a parent is:"
pp cost_of_billing_items_without_parent
end
end
end
After the above I make calls to getVirtualGuests and getHardware to get some additional meta information for each machine (I tie them together based on billingItem.id. Example:
billingItemId = billing_machine["billingItemId"]
account_service = softlayer_client.service_named("Account")
filter = SoftLayer::ObjectFilter.new {|f| f.accept("virtualGuests.billingItem.id").when_it is(billingItemId)}
virtual_guests_array = account_service.object_filter(filter).object_mask("mask[id, hostname, datacenter[name], billingItem[orderItem[order[userRecord[username]]]], tagReferences[tagId, tag[name]], primaryIpAddress, primaryBackendIpAddress]").getVirtualGuests()
As you can see I don't make any calls to capture bandwith overage charges. I have printed out the various "category" values I get from the above query but I am not seeing anything specific to network utilization (it's possible there are no extra network utilization costs but I am not certain).
Thank you.

Any extra costs such as bandwidth overages will be included in the billing item from the server. So you don't need to make any other call to the api to get it.

Related

How to improve Ruby structure for Shopify Script Performance

I'm using a Ruby in Shopify Scripts Editor to manage as a security measure Gift With Purchase (GWP) promotions.
The script current is:
Checking if the Customer is logged in as a Professional or Unlogged
Checking if there is a minimum amount spent in the cart
Ensuring that only one "Gift" product is been added to the cart
Removing a "Gift" product if the checkout doesn't have a "Discount Code" or the minimum set in the GWP_SETTINGS = [] obj.
The problem is that it's generating too much Production Errors like "Your script exceeded the time limit." and "Your script exceeded the cpu limit."
The current usage is CPU: 5% | Memory: 8% and it's increasing dizzyingly every time we add a new GWP promotion array.
Is there a better way to structure this logic so it takes less memory to process the entire order + GWP validation?
Here is the "Line Items" structure:
cart = Input.cart
PRO_TAG = 'professional-tag'
has_pro_tag = cart.customer && cart.customer.tags.include?(PRO_TAG)
GWP_SETTINGS = [
gwp_1 = {
"variant_id" => 98989898989898,
"discount_code" => "DISCOUNT_CODE_1",
"minimum_requirement" => Money.new(cents: 50 * 100),
"user_type" => "consumer"
},
gwp_2 = {
"variant_id" => 97979797979797,
"discount_code" => "DISCOUNT_CODE_1",
"minimum_requirement" => Money.new(cents: 50 * 100),
"user_type" => "consumer"
},
gwp_3 = {
"variant_id" => 96969696969696,
"discount_code" => "DISCOUNT_CODE_1",
"minimum_requirement" => Money.new(cents: 50 * 100),
"user_type" => "consumer"
}
]
def remove_GWP(cart, variant_id)
cart.line_items.each do |item|
next if item.variant.id != variant_id
index = cart.line_items.find_index(item)
cart.line_items.delete_at(index)
end
end
def ensure_only_one_GWP_is_added(cart, variant_id)
cart.line_items.each do |item|
next if item.variant.id != variant_id
item.instance_variable_set(:#quantity, 1)
end
end
GWP_SETTINGS.each do |gwp_item_settings|
customer_has_discount = cart.discount_code && cart.discount_code.code == gwp_item_settings["discount_code"]
customer_has_minimum = cart.subtotal_price >= gwp_item_settings["minimum_requirement"]
gwp_is_for_professional = gwp_item_settings["user_type"] == "professional-tag"
#UNLOGGED
if customer_has_discount && customer_has_minimum
ensure_only_one_GWP_is_added(cart, gwp_item_settings["variant_id"])
else
remove_GWP(cart, gwp_item_settings["variant_id"])
end
#PRO
if gwp_is_for_professional && has_pro_tag
if customer_has_discount && customer_has_minimum
ensure_only_one_GWP_is_added(cart, gwp_item_settings["variant_id"])
else
remove_GWP(cart, gwp_item_settings["variant_id"])
end
end
end
Output.cart = cart
You only have 3 settings. But a customer (an order) could have 100+ line items. You know there is only ever 1 customer, 1 order and for you, 3 GWT settings to use.
Your business logic would be smarter if you looped through the line items only once. Then you have a "this is as fast as I can go, go to town" in terms of your algorithm. You cannot go faster than that.
With things like, "does this customer have an X or Y?", you do those once, not 3 times per line item!
As you check each line item, you can do your special logic for things that might AFFECT that line item.
Basically, this is basic algorithmics. You are doing the most work possible repetitively for no reason, and Shopify is puking because of it.

Compare keys of array in RUBY

I have this structure:
$ArrayX = [8349310431,8349314513,......]
$ArrayY = [667984788,667987788,......]
$ArrayZ = [148507632380,153294624079,.....]
$range_map = $ArrayX.zip([$ArrayY.map(&:to_i),
$ArrayZ.map(&:to_i)].transpose).sort
puts $range_map ={[8349310431=>[667984788, 148507632380],
8349314513=>[667987788, 153294624079]}
I need the key to be compared with the rest of the keys and if the subtraction between keys is lower than 100, that key to print
I corrected your code also as per your need, and solved further,
$ArrayX = [8349310431,8349314513]
$ArrayY = [667984788,667987788]
$ArrayZ = [148507632380,153294624079]
$range_map = $ArrayX.zip([$ArrayY.map(&:to_i), $ArrayZ.map(&:to_i)].transpose).sort
$ArrayX = [8349310431,8349314513]
=> [8349310431, 8349314513]
$ArrayY = [667984788,667987788]
=> [667984788, 667987788]
$ArrayZ = [148507632380,153294624079]
=> [148507632380, 153294624079]
$range_map = Hash[$ArrayX.zip([$ArrayY.map(&:to_i), $ArrayZ.map(&:to_i)].transpose).sort]
=> {8349310431=>[667984788, 148507632380], 8349314513=>[667987788, 153294624079]}
keys = $range_map.keys
valid_keys = keys.select { |k| keys.detect { |x| (x-k).abs > 100 } }
$range_map.slice(*valid_keys)
If particular key is having difference more than 100 with one of rest of keys then it will be valid for filtering.

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

Parsing a string field

I have these Syslog messages:
N 4000000 PROD 15307 23:58:12.13 JOB78035 00000000 $HASP395 GGIVJS27 ENDED\r
NI0000000 PROD 15307 23:58:13.41 STC81508 00000200 $A J78036 /* CA-JOBTRAC JOB RELEASE */\r
I would like to parse these messages into various fields in a Hash, e.g.:
event['recordtype'] #=> "N"
event['routingcode'] #=> "4000000"
event['systemname'] #=> "PROD"
event['datetime'] #=> "15307 23:58:12.13"
event['jobid'] #=> "JOB78035"
event['flag'] #=> "00000000"
event['messageid'] #=> "$HASP395"
event['logmessage'] #=> "$HASP395 GGIVJS27 ENDED\r"
This is the code I have currently:
message = event["message"];
if message.to_s != "" then
if message[2] == " " then
array = message.split(%Q[ ]);
event[%q[recordtype]] = array[0];
event[%q[routingcode]] = array[1];
event[%q[systemname]] = array[2];
event[%q[datetime]] = array[3] + " " +array[4];
event[%q[jobid]] = message[38,8];
event[%q[flags]] = message[47,8];
event[%q[messageid]] = message[57,8];
event[%q[logmessage]] = message[56..-1];
else
array = message.split(%Q[ ]);
event[%q[recordtype]] = array[0][0,2];
event[%q[routingcode]] = array[0][2..-1];
event[%q[systemname]] = array[1];
event[%q[datetime]] = array[2] + " "+array[3];
event[%q[jobid]] = message[38,8];
event[%q[flags]] = message[47,8];
event[%q[messageid]] = message[57,8];
event[%q[logmessage]] = message[56..-1];
end
end
I'm looking to improve the above code. I think I could use a regular expression, but I don't know how to approach it.
You can't use split(' ') or a default split to process your fields because you are dealing with columnar data that has fields that have no whitespace between them, resulting in your array being off. Instead, you have to pick apart each record by columns.
There are many ways to do that but the simplest and probably fastest, is indexing into a string and grabbing n characters:
'foo'[0, 1] # => "f"
'foo'[1, 2] # => "oo"
The first means "starting at index 0 in the string, grab one character." The second means "starting at index 1 in the string, grab two characters."
Alternately, you could tell Ruby to extract by ranges:
'foo'[0 .. 0] # => "f"
'foo'[1 .. 2] # => "oo"
These are documented in the String class.
This makes writing code that's easily understood:
record_type = message[ 0 .. 1 ].rstrip
routing_code = message[ 2 .. 8 ]
system_name = message[ 10 .. 17 ]
Once you have your fields captured add them to a hash:
{
'recordtype' => record_type,
'routingcode' => routing_code,
'systemname' => system_name,
'datetime' => date_time,
'jobid' => job_id,
'flags' => flags,
'messageid' => message_id,
'logmessage' => log_message,
}
While you could use a regular expression there's not much gained using one, it's just another way of doing it. If you were picking data out of free-form text it'd be more useful, but in columnar data it tends to result in visual noise that makes maintenance more difficult. I'd recommend simply determining your columns then cutting the data you need based on those from each line.

how can I get details of a second leg call with twilio?

I want to get the call details, from a second leg call, and insert them into my database.
Here is the scenario: an inbound call to a toll-free number is routed to second phone. So there are 2 legs, 1) the inbound call to the toll-free number and then 2) connection to the second number.
The code for getting the call details for the FIRST leg is:
get '/hangup' do
user_key = numbers.where(:number => params["To"]).join(:credentials, :user_id => :user_id).get(:user_key)
user_token = numbers.where(:number => params["To"]).join(:credentials, :user_id => :user_id).get(:user_token)
call_sid = params["CallSid"]
call_parent_sid = ["ParentCallSid"]
#sub_account_client = Twilio::REST::Client.new(user_key, user_token)
#subaccount = #sub_account_client.account
call = #subaccount.calls.get(call_sid)
call_sid = call.sid,
call_parent_sid = call.parent_call_sid,
phone_number_id = call.phone_number_sid,
call_from = call.from,
call_to = call.to,
call_start = call.start_time,
call_end = call.end_time,
call_duration = call.duration,
charged_duration = ((call_duration.to_f)/60).ceil
call_price = call.price
call_charged_price = (charged_duration * 0.07)
call_logs.insert(:call_sid => call_sid, :call_parent_sid => call_parent_sid, :phone_number_id => phone_number_id, :call_from => call_from, :call_to => call_to, :call_start => call_start, :call_end => call_end, :call_duration => call_duration, :charged_duration => charged_duration, :call_price => call_price, :call_charged_price => call_charged_price)
end
This works after hangup and the status_callback_url is '/hangup'. But how can I get the same details for the second leg of the call. I have tried as follows:
get '/receive' do
destination_number = numbers.where(:number => params["To"]).join(:users, :id => :user_id).get(:primary_number)
user_id = numbers.where(:number => params["To"]).join(:users, :id => :user_id).get(:id)
greeting_url = voicemail.where(:user_id => user_id).get(:voicemail_play_url)
resp = Twilio::TwiML::Response.new do |r|
r.Dial destination_number, :status_callback => '/hangup_second_leg', :status_callback_method => 'GET'
etc..
This effectively attempts to create a second status_callback_url which, needless to say, did not work.
So, how can I get the details of the second (or even third) leg of a call and bung it into my DB?
Twilio evangelist .....
Many thanks in advance.
Twilio developer evangelist at your service!
I just ran a quick test and the parameters you get back from the hangup callback should include a "CallSid" and a "DialedCallSid" which are the two legs of your call. You can get hold of the data via normal calls to the REST api:
get '/hangup' do
call_sid = params["CallSid"]
dialed_call_sid = params["DialedCallSid"]
#sub_account_client = Twilio::REST::Client.new(user_key, user_token)
#subaccount = #sub_account_client.account
inbound = #subaccount.calls.get(call_sid)
outbound = #subaccount.calls.get(dialed_call_sid)
# Update calls in database
end
Alternatively, the inbound call is the parent of all the other calls that take part within the context of the call. So you can get the details on all the child calls with the following api calls:
#subaccount.calls.list parent_call_sid: params["CallSid"]
# => [<Twilio::REST::Call>, ...]
Also, if you are getting a ParentCallSid parameter in your hangup, then you can use the above code to look up the parent call and child calls from that too.
Hope this helps, let me know if there's anything else I can help with.

Resources