Im working on a sinatra based application, in which I get events from a google calendar and display all events as a list. However I have encountered an unusual error when I tried to get the start and end dates of All day events.
Since all day events have an object of type Date, and timed events have an object of type dateTime , these two objects won't get displayed, the error I get is:
no such method named dateTime
It works fine when there is only timed events (dateTime object) events but not when there is an all day event (date object).
Any help would be great.
Code:
require 'rubygems'
require 'google/api_client'
require 'date'
# modified from 1m
# Update these to match your own apps credentials
service_account_email = "" # Email of service account
key_file = "" # File containing your private key
key_secret = 'notasecret' # Password to unlock private key
# Get the Google API client
client = Google::APIClient.new(:application_name => 'GCalendar',
:application_version => '1.0.0')
# Load your credentials for the service account
key = Google::APIClient::KeyUtils.load_from_pkcs12(key_file, key_secret)
client.authorization = Signet::OAuth2::Client.new(
:token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
:audience => 'https://accounts.google.com/o/oauth2/token',
:scope => 'https://www.googleapis.com/auth/calendar',
:issuer => service_account_email,
:signing_key => key)
motd = Array.new
summary = ""
description = ""
tickerEvent = " "
# Start the scheduler
# Request a token for our service account
client.authorization.fetch_access_token!
# Get the calendar API
calendar = client.discovered_api('calendar','v3')
today = DateTime.now().to_s
# Execute the query
result = client.execute(:api_method => calendar.events.list,
:parameters => {'calendarId' => 'idNo',
'timeMin' => today,
'singleEvents' => true,
'orderBy' => 'startTime'})
events = result.data.items
events.each do |e|
if(DateTime.now() >= e.start.dateTime)
summary = e.summary
description = e.description
tickerEvent = summary.to_s + " - " +description.to_s
motd.push(tickerEvent)
elsif(DateTime.now() >= e.end.dateTime)
motd.delete(e)
end
end
motd.clear
end
Is there a way to check whether the event is a Date type or a DateTime type?
In the google api the start dateTime and end dateTime looks like (This is a timed event):
"start": {
"dateTime": "2013-12-03T12:30:00+13:00",
"timeZone": "Pacific/Auckland"
},
"end": {
"dateTime": "2013-12-03T13:00:00+13:00",
"timeZone": "Pacific/Auckland"
},
whereas an all day event looks like:
"start": {
"date": "2014-01-17"
},
"end": {
"date": "2014-01-18"
},
they are of different types, this is what is causing the error.
Cheers
The method is event.start.date_time not event.start.dateTime.
For events that do not have a time associated with them, event.start.date_time will return nil. You can then call event.start.date, which should return just the date.
Related
Hi I'm curious of how to best calculate the price including VAT for a product pre checkout. Currently the only way I found is creating a order containing the item and then fetching the tax and price from the order. This however creates a lot of redundant orders and seems sub optimal. Is there a way to do this calculation without creating an order?
def get_price
location = current_user.location
location_data = APP_CONFIG['country_list'][location]
currency = location_data['currency']
country_code = location_data['code']
product_id = APP_CONFIG['stripe_reconstruction_ids'][currency]
product = Stripe::Product.retrieve(product_id)
product_sku = product['skus']['data'][0]['id']
ip = request.remote_ip
order = Stripe::Order.create(
:currency => currency,
:customer => current_user.stripe_id,
:items => [
{
:type => 'sku',
:parent => product_sku,
:quantity => 1,
:currency => currency
}
],
:email => current_user.email,
:metadata => {
:buyer_ip => ip,
:billing_country_code => country_code,
:product_type => 'e-service'
}
)
render :json => order, :status => 200 and return
rescue => error
logger.error error.message
render :json => { message: "Could not fetch the correct price." }, :status => 500 and return
end
UPDATE
After talking to the stripe support my proposal seems to be the best way to do this at the moment. I suggested to them that it would be nice if developers could set a flag on the order that it was just for pricing information to avoid creating an order that would not later be used for a payment. They said they would deliver this suggestion to their developers. Perhaps we will have a better way to do this in the future.
You can use Taxamo's API Calculate tax endpoint (Ruby client lib is available).
Note that if setting buyer_ip private token must be used, otherwise field is ignored.
Example curl call:
curl -X 'POST' -H "Content-Type: application/json" -d \
'{
"transaction": {
"currency_code": "EUR",
"transaction_lines": [
{
"custom_id": "line1",
"product_type": "e-service",
"amount": 100
}
],
"billing_country_code": "SOME_COUNTRY_CODE",
"buyer_ip": "SOME_IP"
},
"private_token": "YOUR_PRIVATE_TOKEN"
}' \
https://api.taxamo.com/api/v1/tax/calculate | python -m json.tool
I'm using mandrill to send emails from my API (in ruby with the mandrill-api gem), like signup confirmation email.
I have a merge tag in the template to put the username :
Hello *|USERNAME|*,
Thank you very much for installing the app! ...
...
My ruby code looks like that :
m = Mandrill::API.new ENV['MANDRILL_KEY']
template_name = "app-registration-welcome-email"
template_content = [{}]
message = {
:from_name=> "From Name",
:to=>[
{
:email => user.email,
:name => user.name,
}
],
:global_merge_vars => [{
:name => "username",
:content => user.name
}],
:merge_language => "mailchimp",
:merge => true,
:merge_vars => [{
:rcpt => user.email,
:vars => [{
:name => "username",
:content => user.name
}],
}],
:track_opens => true,
}
m.messages.send_template template_name, template_content, message
Unfortunately, when i receive the email, all is fine (to, name, from, ...) but the merge tag isn't replaced in the body and i still have *|USERNAME|* displayed.
What am I missing here?
You should only need to set USERNAME once, either in :global_merge_vars or :merge_vars.
If you're sending to just one recipient, it doesn't matter which you do.
If there are multiple recipients, use :merge_vars.
Try building message and then puts message.to_json and drop it into the debugger at https://mandrillapp.com/api/docs/messages.JSON.html#method=send-template (Click the 'Try it' button.) See if that gives you any clues.
You might also try using "USERNAME" instead of "username" as your variable name. The docs say merge vars are case-insensitive, but it's worth removing one more possible mismatch.
Im trying to save a Point with float value into fitness.body.
Getting value is not a problem, while saving a new point causes 403. No permission to modify data for this source.
Im using DataSetId derived:com.google.weight:com.google.android.gms:merge_weight to find point and read value, and raw:com.google.weight:com.google.android.apps.fitness:user_input to insert data.
.
Here is a workflow using Ruby and google-api-ruby-client:
require 'google/api_client'
require 'google/api_client/client_secrets'
require 'google/api_client/auth/installed_app'
require 'pry'
# Initialize the client.
client = Google::APIClient.new(
:application_name => 'Example Ruby application',
:application_version => '1.0.0'
)
fitness = client.discovered_api('fitness')
# Load client secrets from your client_secrets.json.
client_secrets = Google::APIClient::ClientSecrets.load
flow = Google::APIClient::InstalledAppFlow.new(
:client_id => client_secrets.client_id,
:client_secret => client_secrets.client_secret,
:scope => ['https://www.googleapis.com/auth/fitness.body.write',
'https://www.googleapis.com/auth/fitness.activity.write',
'https://www.googleapis.com/auth/fitness.location.write']
)
client.authorization = flow.authorize
Forming my new data Point:
dataSourceId = 'raw:com.google.weight:com.google.android.apps.fitness:user_input'
startTime = (Time.now-1).to_i # 1 Second ago
endTime = (Time.now).to_i
metadata = {
dataSourceId: dataSourceId,
maxEndTimeNs: "#{startTime}000000000", # Faking nanoseconds with tailing zeros
minStartTimeNs: "#{endTime}000000000",
point: [
{
endTimeNanos: "#{endTime}000000000",
startTimeNanos: "#{startTime}000000000",
value: [
{ fpVal: 80 }
]
}
]
}
Attempting to save the point:
result = client.execute(
:api_method => fitness.users.data_sources.datasets.patch,
:body_object => metadata,
:parameters => {
'userId' => "me",
'dataSourceId' => dataSourceId,
'datasetId' => "#{Time.now.to_i-1}000000000-#{(Time.now).to_i}000000000"
}
)
And as I indicated previously im getting 403. No permission to modify data for this source
#<Google::APIClient::Schema::Fitness::V1::Dataset:0x3fe78c258f60 DATA:{"error"=>{"er
rors"=>[{"domain"=>"global", "reason"=>"forbidden", "message"=>"No permission to modif
y data for this source."}], "code"=>403, "message"=>"No permission to modify data for
this source."}}>
I believe, I selected all required permissions in the scope. I tried submitting the point to both accessible datasetid's for fitness.body.
Please let me know if im doing anything wrong here.
Thank you!
I encountered the same situation, turns out you can NOT insert data points directly into the datasource "raw:com.google.weight:com.google.android.apps.fitness:user_input". From the name, one might guess out this datasource is reserved. So the workaround is to add your own datasource, note should with dataType.name="com.google.weight", like this:
{
"dataStreamName": "xxxx.body.weight",
"dataType": {
"field": [
{
"name": "weight",
"format": "floatPoint"
}
],
"name": "com.google.weight"
},
"dataQualityStandard": [],
"application": {
"version": "1",
"name": "Foo Example App",
"detailsUrl": "http://example.com"
},
"device": {
"model": "xxxmodel",
"version": "1",
"type": "scale",
"uid": "xxx#yyy",
"manufacturer": "xxxxManufacturer"
},
"type": "derived"
}
then after the successful creation, you can use this datasource(datastream id) to insert your own data points, and then the inserted data points will also be included in the datasource "derived:com.google.weight:com.google.android.gms:merge_weight" when you do the querying with suffix "dataPointChanges".
Try adding an Authorization header:
result = client.execute(
:api_method => fitness.users.data_sources.datasets.patch,
:headers => {'Authorization' => 'Bearer YOUR_AUTH_TOKEN'},
:body_object => metadata,
:parameters => {
'userId' => "me",
'dataSourceId' => dataSourceId,
'datasetId' => "#{Time.now.to_i-1}000000000-#{(Time.now).to_i}000000000"
}
)
Hi I was late night hacking and testing google api client for Ruby.
I fell in an error perhaps by missunderstanding.
What I already done
I already created an app in google developer console
I enabled the calendar api
I created the Server Key and downloaded the p12.
I managed to see that calendar metadata using the google api explorer and browser oauth, so the ids are confirmed and the permissions too as browser's oauth can view the metadata
What I'm triying to do:
I'm triying to get metadata (I will try events later) from a privately shared calendar where I have read permissions (trough a group) in a google apps for work account in Ruby on a service to service auth
How I'm triying to do it
With this code:
require 'google/api_client'
require 'pp'
client = Google::APIClient.new
cal = client.discovered_api('calendar','v3')
id = 'xxxxxxxxxxxxxxxxxxxxxx#group.calendar.google.com' # id is the confirmed calendar ID
key = Google::APIClient::KeyUtils.load_from_pkcs12('sl.p12', 'notasecret')
client.authorization = Signet::OAuth2::Client.new(
:token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
:audience => 'https://accounts.google.com/o/oauth2/token',
:scope => 'https://www.googleapis.com/auth/calendar',
:issuer => 'xxxxxxxxxxxxxxxxxxxxxxxxxxx#developer.gserviceaccount.com', # confirmed issuer
:signing_key => key
)
client.authorization.fetch_access_token!
result = client.execute(
:api_method => cal.calendars.get,
:parameters => { 'calendarId' => id }
)
puts result.response.body
pp result
Results colected
When I do this y get a 404, something like "that calendar does not exists"
{
"error": {
"errors": [
{
"domain": "global",
"reason": "notFound",
"message": "Not Found"
}
],
"code": 404,
"message": "Not Found"
}
}
But if i change id per 'primary'
I get:
{
"kind": "calendar#calendar",
"etag": "\"dAAhx6wYoPw2vqRAe54lk5wa0XQ/WEglF6_c5pVHKyggcENvvX1cS9g\"",
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx#developer.gserviceaccount.com", #same as issuer id ??? WTF
"summary": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx#developer.gserviceaccount.com",
"timeZone": "UTC"
}
Which seems to be a calendar but it's like the default calendar for the "email issuer" account that appears in the server key "email" field
I also tried to ad a :person = 'email' field to client.authorization but then I get an error creating the auth token
I couldn't find a way to access the api as other mail account dirrerent from that #developer.gserviceaccount.com, so what I'm doing wrong?
You can either share the calendar with the service account's email or follow these steps to allow the service account to impersonate any user on that domain, in which case you have to pass the :person => 'email' parameter.
I'm working on an App where a user A can buy up to 10 items from different sellers, so I need to send money to different users at the same time and I'm trying to use Paypal Chained Payments.
Right now I'm just playing around with Classic API (Adaptive payments) but I'm just wondering why I'm always getting this error:
"The fee payer PRIMARYRECEIVER can only be used if a primary receiver is specified"
I already specified a primary receiver and I'm still getting that error.
I found these examples: https://paypal-sdk-samples.herokuapp.com/adaptive_payments/pay
and I tried to do a chained payment:
This is my pay request:
require 'paypal-sdk-adaptivepayments'
#api = PayPal::SDK::AdaptivePayments::API.new
# Build request object
#pay = #api.build_pay({
:actionType => "PAY",
:cancelUrl => "https://paypal-sdk-samples.herokuapp.com/adaptive_payments/pay",
:currencyCode => "USD",
:feesPayer => "PRIMARYRECEIVER",
:ipnNotificationUrl => "https://paypal-sdk-samples.herokuapp.com/adaptive_payments/ipn_notify",
:receiverList => {
:receiver => [{
:amount => 1.0,
:email => "platfo_1255612361_per#gmail.com",
:primary => true }] },
:returnUrl => "https://paypal-sdk-samples.herokuapp.com/adaptive_payments/pay",
:sender => {
:useCredentials => true } })
# Make API call & get response
#pay_response = #api.pay(#pay)
And this is the response
{
:responseEnvelope => {
:timestamp => "2013-11-20T05:16:31-08:00",
:ack => "Failure",
:correlationId => "b002d0e27fd33",
:build => "7935900" },
:error => [{
:errorId => 580023,
:domain => "PLATFORM",
:subdomain => "Application",
:severity => "Error",
:category => "Application",
:message => "The fee payer PRIMARYRECEIVER can only be used if a primary receiver is specified",
:parameter => [{
:value => "feesPayer" },{
:value => "PRIMARYRECEIVER" }] }] }
Thanks in advance!
taking a look at your call, you're indeed specifying a primary receiver. however, for chained payment, you will have to specify a secondary receiver.
I just ran a quick test with the following paramters:
actionType = PAY
requestEnvelope.errorLanguage = en_US
cancelUrl = http://abortURL
returnUrl = http://returnURL
ipnNotificationUrl = http://ipnURL
applicationId = Test
memo = Test
currencyCode = USD
receiverList.receiver(0).email = test#test.com
receiverList.receiver(0).amount = 5.00
receiverList.receiver(0).primary = true
feesPayer = PRIMARYRECEIVER
and got the result:
responseEnvelope.timestamp=2013-11-20T05:41:56.751-08:00
responseEnvelope.ack=Failure
responseEnvelope.correlationId=b61a6b31ea2ab
responseEnvelope.build=7935900
error(0).errorId=580023
error(0).domain=PLATFORM
error(0).subdomain=Application
error(0).severity=Error
error(0).category=Application
error(0).message=The fee payer PRIMARYRECEIVER can only be used if a primary receiver is specified
error(0).parameter(0)=feesPayer
error(0).parameter(1)=PRIMARYRECEIVER
However, once I change the FeesPayer to EACHRECEIVER, I get the error message that is causing the chained payment to fail in the first place:
responseEnvelope.timestamp=2013-11-20T05:48:09.202-08:00
responseEnvelope.ack=Failure
responseEnvelope.correlationId=987210ec4d03a
responseEnvelope.build=7935900
error(0).errorId=579008
error(0).domain=PLATFORM
error(0).subdomain=Application
error(0).severity=Error
error(0).category=Application
error(0).message=You must specify only one primary receiver and at least one secondary receiver
error(0).parameter(0)=1
I hope this helps.
Please refer to the PayPal Adaptive Payments SDK available under http://paypal.github.io/#adaptive-payments for some additional examples and inspiration