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] }


:: #{ story[:type] } activity


#{ url }" msg[:tags] = ["asana"] # digest = Digest::MD5.hexdigest(msg.to_json) # send message to the flow pushed = flow.push_to_team_inbox(msg) binding.pry end end puts end end end __END__ [{"id"=>18125730607362, "created_at"=>"2014-10-17T19:02:32.738Z", "created_by"=>{"id"=>2636700963611, "name"=>"Ara Howard"}, "type"=>"system", "text"=>"added to www"}, {"id"=>18125730607364, "created_at"=>"2014-10-17T19:02:58.466Z", "created_by"=>{"id"=>2636700963611, "name"=>"Ara Howard"}, "type"=>"system", "text"=>"assigned to steve"}, {"id"=>18199820086403, "created_at"=>"2014-10-19T01:40:37.796Z", "type"=>"comment", "text"=> "Very generic page template and specific donation template!\n\nhttps://github.com/dojo4/uscm", "created_by"=>{"id"=>17675380473837, "name"=>"steve"}}, {"id"=>18199846750605, "created_at"=>"2014-10-19T01:48:18.020Z", "type"=>"comment", "text"=> "Can use some refinement and probably additions, but in a good place to wire up and show off...", "created_by"=>{"id"=>17675380473837, "name"=>"steve"}}, {"id"=>18199848953716, "created_at"=>"2014-10-19T01:49:50.353Z", "type"=>"system", "text"=>"completed this task", "created_by"=>{"id"=>17675380473837, "name"=>"steve"}}]