vworkapp_ruby 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+