vworkapp_ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in vworkapp.gemspec
4
+ gemspec
@@ -0,0 +1,15 @@
1
+ # vWorkApp
2
+
3
+ This is a wrapper around vWorkApp's API. Go on! Use it!
4
+
5
+ VWorkApp::Job.api_key = "MY_API_KEY"
6
+ jobs = VWorkApp::Job.find(:worker_id => 1971, :state => "not_started")
7
+
8
+ ## Todo
9
+
10
+ - Handle paging
11
+
12
+ - Broken API bits:
13
+ * What's find(:search) for??.
14
+ * customer_name broken?
15
+ * Why can't I filter by template?
@@ -0,0 +1,34 @@
1
+ # -------------
2
+ # Example: Create job
3
+ # -------------
4
+ # Imports a list of jobs from Google Docs and creates them in vWorkApp
5
+ #
6
+
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib') unless $LOAD_PATH.include?(File.dirname(__FILE__) + '/../lib')
8
+ require "rubygems"
9
+ require "vworkapp_ruby"
10
+
11
+ MIN = 60
12
+ HOUR = 60 * MIN
13
+
14
+ VWorkApp.api_key = "AtuogECLCV2R7uT-fkPg"
15
+
16
+ puts <<-EOL
17
+ ----------------------------
18
+ vWorkApp - Create Job
19
+ ----------------------------
20
+ EOL
21
+
22
+ job = VWorkApp::Job.new(
23
+ "ACME Inc.",
24
+ "Standard Booking",
25
+ 10 * HOUR,
26
+ [
27
+ VWorkApp::Step.new("End", VWorkApp::Location.from_address("201 1st Street, SF", :us)),
28
+ VWorkApp::Step.new("Start", VWorkApp::Location.new("880 Harrison St!", 37.779536, -122.401503))
29
+ ],
30
+ [
31
+ VWorkApp::CustomField.new("Note", "Hi There!"),
32
+ ]
33
+ )
34
+ p job.create
@@ -0,0 +1,34 @@
1
+ # -------------
2
+ # Example: Print Jobs
3
+ # -------------
4
+ # Gets all workers, and then prints a list of each's unstarted jobs to the terminal.
5
+ #
6
+
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib') unless $LOAD_PATH.include?(File.dirname(__FILE__) + '/../lib')
8
+ require "rubygems"
9
+ require "vworkapp_ruby"
10
+
11
+ VWorkApp.api_key = "AtuogECLCV2R7uT-fkPg"
12
+
13
+ # MultiXml.parser = :rexml
14
+ # p MultiXml.parser
15
+
16
+ puts <<-EOL
17
+ ----------------------------
18
+ vWorkApp - List of jobs
19
+ ----------------------------
20
+ EOL
21
+ workers = VWorkApp::Worker.all
22
+
23
+ workers.each do |worker|
24
+ puts "Worker: #{worker.name}"
25
+ jobs = VWorkApp::Job.find(:worker_id => worker.id, :state => "not_started")
26
+ jobs.each do |job|
27
+ puts "\t #{job.third_party_id || job.id}, #{job.customer_name}, #{job.template_name}"
28
+ end
29
+ puts ""
30
+ end
31
+
32
+ # p jobs = VWorkApp::Job.find(:worker_id => 4228)["cars"]
33
+
34
+ # p job = VWorkApp::Job.show(180022)
@@ -0,0 +1,8 @@
1
+ require 'vworkapp_ruby/base'
2
+ require 'vworkapp_ruby/job'
3
+ require 'vworkapp_ruby/worker'
4
+ require 'vworkapp_ruby/customer'
5
+ require 'vworkapp_ruby/response_error'
6
+ # require 'vworkapp/proof_of_delivery'
7
+
8
+ require 'vworkapp_ruby/httparty_monkey_patch'
@@ -0,0 +1,40 @@
1
+ require 'httparty'
2
+
3
+ module VWorkApp
4
+
5
+ class << self
6
+ @api_key = "YOU NEED TO SPECIFY YOUR API KEY!!"
7
+
8
+ def api_key=(key)
9
+ @api_key = key
10
+ end
11
+ def api_key
12
+ @api_key
13
+ end
14
+ end
15
+
16
+ class Base
17
+
18
+ include HTTParty
19
+
20
+ base_uri 'api.vworkapp.com/api/2.0'
21
+ # http_proxy 'localhost', 8888
22
+ # format :xml
23
+
24
+ headers({
25
+ "User-Agent" => "Ruby.vWorkApp.API"
26
+ })
27
+
28
+ # Examines a bad response and raises an approriate exception
29
+ #
30
+ # @param [HTTParty::Response] response
31
+ def self.bad_response(response)
32
+ if response.class == HTTParty::Response
33
+ raise ResponseError, response
34
+ end
35
+ raise StandardError, "Unkown error"
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,5 @@
1
+ vwk-job find -w 12 -c Bob
2
+ vwk-job show -w 12 -c Bob
3
+ vwk-job create -worker 12 --customer Bob --template Delivery --start-time --duration 12 --cf name1:value1,name1:value1
4
+ vwk-job list -w 12 -c Bob
5
+ vwk-job delete -w 12 -c Bob
File without changes
@@ -0,0 +1,67 @@
1
+
2
+ ##
3
+ # Needed because the extra attributes on the below XML cause httparty to crash:
4
+ # <jobs type="array" current_page="1" per_page="27" total_pages="1" total_entries="27">
5
+ #
6
+ # We probably shouldn't have extra parameters here.
7
+ #
8
+ # && v.is_a?(Array)
9
+
10
+ module MultiXml
11
+
12
+ def MultiXml.typecast_xml_value(value)
13
+ case value
14
+ when Hash
15
+ if value['type'] == 'array'
16
+ _, entries = value.detect { |k, v| k != 'type' && (v.is_a?(Array) || v.is_a?(Hash)) }
17
+
18
+ if entries.nil? || (entries.is_a?(String) && entries.strip.empty?)
19
+ []
20
+ else
21
+ case entries
22
+ when Array
23
+ entries.map {|entry| typecast_xml_value(entry)}
24
+ when Hash
25
+ [typecast_xml_value(entries)]
26
+ else
27
+ raise "can't typecast #{entries.class.name}: #{entries.inspect}"
28
+ end
29
+ end
30
+ elsif value.has_key?(CONTENT_ROOT)
31
+ content = value[CONTENT_ROOT]
32
+ if block = PARSING[value['type']]
33
+ block.arity == 1 ? block.call(content) : block.call(content, value)
34
+ else
35
+ content
36
+ end
37
+ elsif value['type'] == 'string' && value['nil'] != 'true'
38
+ ''
39
+ # blank or nil parsed values are represented by nil
40
+ elsif value.empty? || value['nil'] == 'true'
41
+ nil
42
+ # If the type is the only element which makes it then
43
+ # this still makes the value nil, except if type is
44
+ # a XML node(where type['value'] is a Hash)
45
+ elsif value['type'] && value.size == 1 && !value['type'].is_a?(Hash)
46
+ nil
47
+ else
48
+ xml_value = value.inject({}) do |hash, (k, v)|
49
+ hash[k] = typecast_xml_value(v)
50
+ hash
51
+ end
52
+
53
+ # Turn {:files => {:file => #<StringIO>} into {:files => #<StringIO>} so it is compatible with
54
+ # how multipart uploaded files from HTML appear
55
+ xml_value['file'].is_a?(StringIO) ? xml_value['file'] : xml_value
56
+ end
57
+ when Array
58
+ value.map!{|i| typecast_xml_value(i)}
59
+ value.length > 1 ? value : value.first
60
+ when String
61
+ value
62
+ else
63
+ raise "can't typecast #{value.class.name}: #{value.inspect}"
64
+ end
65
+ end
66
+
67
+ end
@@ -0,0 +1,162 @@
1
+ require 'gcoder'
2
+
3
+ module VWorkApp
4
+
5
+ class Location
6
+ attr_accessor :formatted_address, :lat, :lng
7
+ def initialize(formatted_address, lat, lng)
8
+ @formatted_address = formatted_address
9
+ @lat = lat
10
+ @lng = lng
11
+ end
12
+
13
+ def self.from_address(address, region = :us)
14
+ loc = Location.geocode(address, region).first.geometry.location
15
+ self.new(address, loc.lat, loc.lng)
16
+ end
17
+
18
+ def to_hash
19
+ { :formatted_address => formatted_address, :lat => lat, :lng => lng }
20
+ end
21
+
22
+ def self.from_hash(attributes)
23
+ return nil unless attributes
24
+ Location.new(attributes["formatted_address"], attributes["lat"], attributes["lng"])
25
+ end
26
+
27
+ private
28
+
29
+ def self.geocode(address, region)
30
+ @gecoder ||= GCoder.connect(:storage => :heap)
31
+ @gecoder[address, { :region => region }]
32
+ end
33
+
34
+ end
35
+
36
+ class Step
37
+ attr_accessor :name, :location
38
+ def initialize(name, location = nil)
39
+ @name = name
40
+ @location = location
41
+ end
42
+
43
+ def to_hash
44
+ h = { :name => name }
45
+ h.merge!({ :location => location.to_hash }) if location
46
+ h
47
+ end
48
+
49
+ def self.from_hash(attributes)
50
+ Step.new(attributes["name"], Location.from_hash(attributes["location"]))
51
+ end
52
+
53
+ end
54
+
55
+ class CustomField
56
+ attr_accessor :name, :value
57
+ def initialize(name, value)
58
+ @name = name
59
+ @value = value
60
+ end
61
+
62
+ def to_hash
63
+ { :name => name.to_s, :value => value.to_s }
64
+ end
65
+
66
+ def self.from_hash(attributes)
67
+ CustomField.new(attributes["name"], attributes["value"])
68
+ end
69
+
70
+ end
71
+
72
+ class Job < Base
73
+
74
+ attr_accessor :id, :customer_name, :template_name, :planned_duration, :steps, :custom_fields, :third_party_id, :assigned_to, :planned_start_at
75
+
76
+ ##
77
+ # Creates a new jobs in vWorkApp.
78
+ #
79
+ # Steps and Custom_Fields takes an array of these.
80
+ #
81
+ def initialize(customer_name, template_name, planned_duration, steps, custom_fields = nil, id = nil, third_party_id = nil, assigned_to = nil, planned_start_at = nil)
82
+ @id = id
83
+ @customer_name = customer_name
84
+ @template_name = template_name
85
+ @planned_duration = planned_duration
86
+ @steps = steps
87
+ @custom_fields = custom_fields
88
+ @third_party_id = third_party_id
89
+ @assigned_to = assigned_to
90
+ @planned_start_at = planned_start_at
91
+ end
92
+
93
+ def is_new?
94
+ self.id == nil
95
+ end
96
+
97
+ def to_hash
98
+ job_hash = {
99
+ :job => {
100
+ :customer_name => self.customer_name,
101
+ :template_name => self.template_name,
102
+ :planned_duration => self.planned_duration,
103
+ :steps => self.steps.map { |s| s.to_hash },
104
+ }
105
+ }
106
+ job_hash[:job][:third_party_id] = self.third_party_id if third_party_id
107
+ job_hash[:job][:custom_fields] = self.custom_fields.map { |s| s.to_hash } if custom_fields
108
+ job_hash
109
+ end
110
+
111
+ def self.from_hash(attributes)
112
+ j = Job.new(attributes["customer_name"], attributes["template_name"], attributes["planned_duration"], attributes["steps"].map { |h| Step.from_hash(h) }, nil, attributes["id"], attributes["third_party_id"])
113
+ j.custom_fields = attributes["custom_fields"].map { |h| CustomField.from_hash(h) } if attributes["custom_fields"]
114
+ j
115
+ end
116
+
117
+ def create
118
+ res = self.class.post("/jobs", :body => self.to_hash, :query => { :api_key => VWorkApp.api_key })
119
+ res.success? ? res : self.class.bad_response(res)
120
+ end
121
+
122
+ def update(id, attributes)
123
+ res = self.class.post("/jobs", :body => { :job => self.to_hash }, :query => { :api_key => VWorkApp.api_key })
124
+ res.success? ? res : bad_response(res)
125
+ end
126
+
127
+ def self.all
128
+ find
129
+ end
130
+
131
+ ##
132
+ # Returns jobs that match the given query options. Valid options are:
133
+ # - :state — Filter by state. Possible values: unallocated, allocated, assigned, not_started, started, completed.
134
+ # - :start_at — Filter by planned_start_at/last_action_time (must also give end_at).
135
+ # - :end_at — Filter by planned_start_at/last_action_time (must also give start_at).
136
+ # - :customer_name — Filter by customer name.
137
+ # - :worker_id — Filter by assigned worker.
138
+ # - :worker_third_party_id — Filter by assigned worker third party id.
139
+ # - :per_page — The number of records to return per page.
140
+ # - :page — The page of records to return.
141
+ # - :third_party_id — The third_party_id associated with the job.
142
+ def self.find(options={})
143
+ p options
144
+ third_party_id = options.delete(:third_party_id)
145
+ options[:search] = "@third_party_id=#{third_party_id}" if (third_party_id)
146
+ options[:api_key] = VWorkApp.api_key
147
+
148
+ raw = get("/jobs.xml", :query => options)
149
+ raw["jobs"].map { |h| Job.from_hash(h) }
150
+ end
151
+
152
+ def self.show(id)
153
+ get("/jobs/#{id}.xml", :query => { :api_key => VWorkApp.api_key })
154
+ end
155
+
156
+ def self.delete(id)
157
+
158
+ end
159
+
160
+ end
161
+
162
+ end
@@ -0,0 +1,35 @@
1
+ module VWorkApp
2
+
3
+ # Error raised on a bad response
4
+ class ResponseError < StandardError
5
+
6
+ attr_reader :response, :code, :errors
7
+
8
+ def initialize(res)
9
+ @response = res.response
10
+ @code = res.code
11
+ @errors = parse_errors(res.parsed_response)
12
+ end
13
+
14
+ def to_s
15
+ p @response.body
16
+ "#{code.to_s} #{response.msg}".strip
17
+ end
18
+
19
+ private
20
+
21
+ def parse_errors(errors)
22
+ return case errors
23
+ when Hash
24
+ errors.collect{|k,v| "#{k}: #{v}"}
25
+ when String
26
+ [errors]
27
+ when Array
28
+ errors
29
+ else []
30
+ end
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,51 @@
1
+ module VWorkApp
2
+
3
+ class Telemetry
4
+ attr_accessor :lat, :lng, :recorded_at, :heading, :speed
5
+ def initialize (lat, lng, recorded_at, heading, speed)
6
+ @lat = lat
7
+ @lng = lng
8
+ @recorded_at = recorded_at
9
+ @heading = heading
10
+ @speed = speed
11
+ end
12
+
13
+ def self.from_hash(attributes)
14
+ @lat = attributes["lat"]
15
+ @lng = attributes["lng"]
16
+ @recorded_at = attributes["recorded_at"]
17
+ @heading = attributes["heading"]
18
+ @speed = attributes["speed"]
19
+ end
20
+
21
+ end
22
+
23
+ class Worker < Base
24
+
25
+ attr_accessor :id, :name, :email, :latest_telemetry, :third_party_id
26
+
27
+ def initialize(name, email, id = nil, third_party_id = nil)
28
+ @name = name
29
+ @email = email
30
+ @id = id
31
+ @third_party_id = third_party_id
32
+ end
33
+
34
+ def self.all
35
+ find
36
+ end
37
+
38
+ def self.find
39
+ raw = get("/workers.xml", :query => { :api_key => VWorkApp.api_key })["workers"]
40
+ raw.map { |h| Worker.from_hash(h) }
41
+ end
42
+
43
+ def self.from_hash(attributes)
44
+ w = Worker.new(attributes["name"], attributes["email"], attributes["id"], attributes["third_party_id"])
45
+ w.latest_telemetry = Telemetry.from_hash(attributes["latest_telemetry"]) if attributes["latest_telemetry"]
46
+ w
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,21 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "vworkapp_ruby"
3
+ s.version = "0.0.1"
4
+ s.platform = Gem::Platform::RUBY
5
+ s.authors = ["vWorkApp Inc.", "Aish Fenton"]
6
+ s.email = ["info@vworkapp.com"]
7
+ s.homepage = "http://api.vworkapp.com/api/"
8
+ s.summary = %q{Simple interface to vWorkApp's api}
9
+ s.description = %q{Simple interface to vWorkApp's api}
10
+
11
+ s.required_rubygems_version = ">= 1.3.6"
12
+
13
+ s.add_dependency "httparty"
14
+ s.add_dependency "gcoder"
15
+ s.add_development_dependency "rspec"
16
+
17
+ s.files = `git ls-files`.split("\n") rescue ''
18
+ s.test_files = `git ls-files -- {test, spec, features}/*`.split("\n")
19
+
20
+ s.require_paths = ["lib"]
21
+ end
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vworkapp_ruby
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - vWorkApp Inc.
14
+ - Aish Fenton
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2011-10-03 00:00:00 Z
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: httparty
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: gcoder
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: rspec
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ type: :development
62
+ version_requirements: *id003
63
+ description: Simple interface to vWorkApp's api
64
+ email:
65
+ - info@vworkapp.com
66
+ executables: []
67
+
68
+ extensions: []
69
+
70
+ extra_rdoc_files: []
71
+
72
+ files:
73
+ - Gemfile
74
+ - README.md
75
+ - examples/create_job.rb
76
+ - examples/print_jobs.rb
77
+ - lib/vworkapp_ruby.rb
78
+ - lib/vworkapp_ruby/base.rb
79
+ - lib/vworkapp_ruby/cli/job.rb
80
+ - lib/vworkapp_ruby/customer.rb
81
+ - lib/vworkapp_ruby/httparty_monkey_patch.rb
82
+ - lib/vworkapp_ruby/job.rb
83
+ - lib/vworkapp_ruby/response_error.rb
84
+ - lib/vworkapp_ruby/worker.rb
85
+ - vworkapp_ruby.gemspec
86
+ homepage: http://api.vworkapp.com/api/
87
+ licenses: []
88
+
89
+ post_install_message:
90
+ rdoc_options: []
91
+
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ hash: 3
100
+ segments:
101
+ - 0
102
+ version: "0"
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ hash: 23
109
+ segments:
110
+ - 1
111
+ - 3
112
+ - 6
113
+ version: 1.3.6
114
+ requirements: []
115
+
116
+ rubyforge_project:
117
+ rubygems_version: 1.8.10
118
+ signing_key:
119
+ specification_version: 3
120
+ summary: Simple interface to vWorkApp's api
121
+ test_files: []
122
+