module Asana
# ref: http://developer.asana.com/documentation/
#
require 'cgi'
require 'uri'
require 'time'
require 'json'
require 'map'
require 'coerce'
require 'fattr'
require 'threadify'
require 'pry'
class Api
#
Uri = URI.parse('https://app.asana.com/api/1.0')
#
fattr(:debug){ false }
fattr(:token)
fattr(:uri){ Uri.dup }
fattr(:cache){ Map.new }
#
def initialize(token = ENV['ASANA_TOKEN'])
@token = token
end
def me
get("/users/me", :model => User)
end
def users(options = {})
get("/users", User, options)
end
alias_method :my, :me
def get(url, *args)
#
url = url.to_s
options = Map.options_for!(args)
model = args.detect{|arg| arg <= Model} || options.delete(:model)
query = options[:query] || options
#
unless url['://']
url = url_for(url)
end
#
uri = URI.parse(url)
unless query.blank?
uri.query = query.map{|k,v| [CGI.escape(k.to_s), CGI.escape(v.to_s)].join('=')}.join('&')
end
url = uri.to_s
#
result = nil
error = nil
cmd = nil
#
3.times do
begin
cmd = "curl -s -u #{ @token }: #{ url.inspect } 2>/dev/null"
if cache[url]
result = cache[url]
else
STDERR.puts cmd if debug?
result = `#{ cmd }`
if $?.exitstatus == 0
cache[url] = result
end
end
rescue => e
error = e
sleep(3)
nil
end
end
#
raise "#{ cmd } blargh'd!?" if error || result.nil?
#
result = JSON.parse(result)
if result['errors']
raise result['errors'].inspect
end
data = result['data']
#
result_for(data, :model => model)
end
def url_for(*paths)
uri = Uri.dup
path = uri.path
uri.path = File.join(path, *paths.flatten.compact.map{|path| path.to_s})
uri.to_s
end
def result_for(data, options = {})
case data
when Array
data.map do |item|
result_for(item, options)
end
when Hash
attributes = Map.for(data)
model = options[:model]
if model
model_for(model, attributes)
else
attributes
end
else
raise ArgumentError, data.class.name
end
end
def model_for(model, *args, &block)
model = model.for(*args, &block)
ensure
model.api = self if model
end
end
#
class Model < ::Map
fattr(:api)
def Model.for(attributes, *args, &block)
new(attributes, *args, &block)
end
def initialize(attributes, *args, &block)
super(attributes)
options = Map.options_for!(args)
if options.has?(:api)
@api = options.delete(:api)
end
end
def inspect(*args, &block)
"#{ self.class.name }(\n#{ super }\n)"
end
%w(
model_for
url_for
).each do |method|
class_eval <<-__
def #{ method }(*args, &block)
api.#{ method }(*args, &block) if api
end
__
end
end
#
class User < Model
def workspaces
Array(self[:workspaces]).map{|attributes| model_for(Workspace, attributes)}
end
def projects(options = {})
options = Map.for(options)
workspaces.map{|workspace| workspace.projects(options)}
end
def tasks(options = {})
options = Map.for(options)
workspaces.map do |workspace|
workspace.tasks(options.merge(:assignee => id))
end.flatten.compact
end
end
#
class Workspace < Model
def projects(options = {})
api.get("/workspaces/#{ id }/projects", Project, options)
end
def tasks(options = {})
options = Map.for(options)
if options.has?(:assignee)
api.get("/tasks", Task, options.merge(:workspace => id))
else
projects.each do |project|
project.tasks(options)
end.flatten.compact
end
end
end
#
class Project < Model
def tasks(options = {})
api.get("/projects/#{ id }/tasks", Task, options)
end
end
#
class Task < Model
def stories(options = {})
api.get("/tasks/#{ id }/stories", Story, options)
end
end
#
class Story < Model
end
#
def Asana.api_for(token)
token = token.to_s
(@apis ||= {})[token] ||= Api.new(token)
end
end
if $0 == __FILE__
asana = Asana.api_for 'Kq4MxiX.05vZDJU5FM60HbifOUKBXrTu'
asana.debug = true
#p asana.users(:opt_fields => 'name,email')
#p asana.users(:opt_fields => 'name,email')
#exit
#p asana.me
#p asana.my.tasks
workspace = asana.my.workspaces.detect{|workspace| workspace.name == 'dojo4.com'}
hrs = 6
ago = (Time.now - (hrs * 60 * 60))
workspace.projects.each do |project|
#p project
project.tasks(:modified_since => ago.iso8601).each do |task|
p task
task.stories(:opt_fields => 'id,created_at,type,text,created_by.name,created_by.email').each do |story|
if Coerce.time(story[:created_at]) >= ago
created_by = story[:created_by] || Map.new
from = Map.new
from[:name] = created_by[:name]
from[:address] = created_by[:email]
url = "https://app.asana.com/0/#{ project.id }/#{ task.id }/f"
require 'flowdock'
# create a new Flow object with target flow's api token and sender information
flow = Flowdock::Flow.new(
:api_token => "7e1c8271ffb6351a6405237d73b83383",
:source => "asana",
:project => project.name,
:from => from
)
msg = Map.new
msg[:format] = "html"
msg[:subject] = "#{ project.name } :: #{ task.name }"
msg[:content] = "#{ story[:text] }