shipment 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -24,23 +24,44 @@ Feature: Shipment Committee Calculations
24
24
  When the "mode" committee is calculated
25
25
  Then the committee should have used quorum "default"
26
26
  And the conclusion of the committee should be a fallback
27
+
28
+ Scenario Outline: Distance from same locality
29
+ Given a shipment emitter
30
+ And a characteristic "origin_zip_code.name" of "<origin>"
31
+ And a characteristic "destination_zip_code.name" of "<destination>"
32
+ When the "distance" committee is calculated
33
+ Then the conclusion of the committee should be "<distance>"
34
+ Examples:
35
+ | origin | destination | distance |
36
+ | 05753 | 05753 | 0 |
37
+ | 05753 | 05401 | |
27
38
 
28
- Scenario: Segment count from nothing
39
+ Scenario Outline: Distance from mapquest
29
40
  Given a shipment emitter
30
- When the "segment_count" committee is calculated
31
- Then the committee should have used quorum "default"
32
- And the conclusion of the committee should be "5"
41
+ And a characteristic "origin_zip_code.name" of "<origin>"
42
+ And a characteristic "destination_zip_code.name" of "<destination>"
43
+ And a characteristic "mode.name" of "ground"
44
+ And a characteristic "mapquest_api_key" of "ABC123"
45
+ And mapquest determines the distance to be "<mapquest_distance>"
46
+ When the "distance" committee is calculated
47
+ Then the committee should have used quorum "from mapquest"
48
+ And the conclusion of the committee should be "<distance>"
49
+ Examples:
50
+ | origin | destination | mapquest_distance | distance |
51
+ | 05753 | 05401 | 53 | 53 |
52
+ | 05753 | 05401 | 33 | 33 |
33
53
 
34
- Scenario Outline: Distance from origin zip code and destination zip code
54
+ Scenario Outline: Distance from direct path
35
55
  Given a shipment emitter
36
56
  And a characteristic "origin_zip_code.name" of "<origin>"
37
57
  And a characteristic "destination_zip_code.name" of "<destination>"
58
+ And a characteristic "mode.name" of "air"
59
+ And a characteristic "mapquest_api_key" of "ABC123"
38
60
  When the "distance" committee is calculated
39
- Then the committee should have used quorum "from origin zip code and destination zip code"
61
+ Then the committee should have used quorum "from direct path"
40
62
  And the conclusion of the committee should be "<distance>"
41
63
  Examples:
42
64
  | origin | destination | distance |
43
- | 05753 | 05753 | 0 |
44
65
  | 05753 | 05401 | 53 |
45
66
 
46
67
  Scenario Outline: Distance from zip codes not in database or missing lat/lng
@@ -73,12 +94,11 @@ Feature: Shipment Committee Calculations
73
94
  | ground | 1.0 |
74
95
  | air | 1.1 |
75
96
 
76
- Scenario: Dogleg factor from default segment count
97
+ Scenario: Dogleg factor from nothing
77
98
  Given a shipment emitter
78
- When the "segment_count" committee is calculated
79
- And the "dogleg_factor" committee is calculated
99
+ When the "dogleg_factor" committee is calculated
80
100
  Then the committee should have used quorum "from segment count"
81
- And the conclusion of the committee should be "5.0625"
101
+ And the conclusion of the committee should be "1.8"
82
102
 
83
103
  Scenario Outline: Dogleg factor from segment count
84
104
  Given a shipment emitter
@@ -89,7 +109,9 @@ Feature: Shipment Committee Calculations
89
109
  Examples:
90
110
  | segments | factor |
91
111
  | 1 | 1.0 |
92
- | 2 | 1.5 |
112
+ | 2 | 1.8 |
113
+ | 100 | 1.8 |
114
+ | -20 | 1.8 |
93
115
 
94
116
  Scenario: Adjusted distance from default
95
117
  Given a shipment emitter
@@ -6,7 +6,7 @@ Feature: Shipment Emissions Calculations
6
6
  When emissions are calculated
7
7
  Then the emission value should be within "0.1" kgs of "8.7"
8
8
 
9
- Scenario: Calculations from weight, package count, segment count
9
+ Scenario: Calculations from weight, package count and segment count
10
10
  Given a shipment has "weight" of "10"
11
11
  And it has "package_count" of "2"
12
12
  And it has "segment_count" of "1"
@@ -16,12 +16,13 @@ Feature: Shipment Emissions Calculations
16
16
  Scenario Outline: Calculations from origin/destination
17
17
  Given a shipment has "origin_zip_code.name" of "<origin>"
18
18
  And it has "destination_zip_code.name" of "<destination>"
19
+ And mapquest determines the distance to be "<distance>"
19
20
  When emissions are calculated
20
21
  Then the emission value should be within "0.1" kgs of "<emission>"
21
22
  Examples:
22
- | origin | destination | emission |
23
- | 05401 | 05401 | 0.3 |
24
- | 05401 | 94128 | 57.8 |
23
+ | origin | destination | mapquest_distance | emission |
24
+ | 05401 | 05401 | | 0.3 |
25
+ | 05401 | 94128 | 30 | 20.8 |
25
26
 
26
27
  Scenario: Calculations from carrier
27
28
  Given a shipment has "carrier.name" of "FedEx"
@@ -50,5 +51,5 @@ Feature: Shipment Emissions Calculations
50
51
  | ground | 05401 | 05401 | 0.3 |
51
52
  | air | 05401 | 05401 | 0.3 |
52
53
  | courier | 05401 | 94128 | 2.3 |
53
- | ground | 05401 | 94128 | 71145.0 |
54
- | air | 05401 | 94128 | 391296.2 |
54
+ | ground | 05401 | 94128 | 25296.2 |
55
+ | air | 05401 | 94128 | 139127.8 |
@@ -0,0 +1,4 @@
1
+ Given /^mapquest determines the distance to be "([^\"]*)"$/ do |distance|
2
+ mockquest = mock MapQuestDirections, :distance_in_miles => distance
3
+ MapQuestDirections.stub!(:new).and_return mockquest
4
+ end
@@ -5,6 +5,7 @@ require 'cucumber'
5
5
  require 'cucumber/formatter/unicode'
6
6
  require 'rspec'
7
7
  require 'rspec/expectations'
8
+ require 'cucumber/rspec/doubles'
8
9
 
9
10
  require 'sniff'
10
11
  Sniff.init File.join(File.dirname(__FILE__), '..', '..'), :cucumber => true, :earth => [:locality, :shipping]
@@ -2,6 +2,8 @@
2
2
  # See LICENSE for details.
3
3
  # Contact Brighter Planet for dual-license arrangements.
4
4
 
5
+ require File.expand_path('../../vendor/plugin/mapquest/lib/mapquest_directions', File.dirname(__FILE__))
6
+
5
7
  ## Shipment carbon model
6
8
  # This model is used by [Brighter Planet](http://brighterplanet.com)'s carbon emission [web service](http://carbon.brighterplanet.com) to estimate the **greenhouse gas emissions of a shipment** (e.g. a FedEx package).
7
9
  #
@@ -76,14 +78,14 @@ module BrighterPlanet
76
78
  3219
77
79
  end
78
80
  end
79
-
81
+
80
82
  committee :dogleg_factor do
81
- quorum 'from segment count', :needs => :segment_count do |characteristics|
82
- if characteristics[:segment_count] > 0
83
- # ASSUMED arbitrary
84
- 1.5 ** (characteristics[:segment_count] - 1)
83
+ quorum 'from segment count', :appreciates => :segment_count do |characteristics|
84
+ if characteristics[:segment_count] == 1
85
+ 1.0
85
86
  else
86
- raise "Invalid segment_count: #{:segment_count} (must be > 0)"
87
+ # based on our sample FedEx tracking numbers
88
+ 1.8
87
89
  end
88
90
  end
89
91
  end
@@ -95,34 +97,26 @@ module BrighterPlanet
95
97
  end
96
98
 
97
99
  committee :distance do
98
- quorum 'from origin zip code and destination zip code', :needs => [:origin_zip_code, :destination_zip_code], do |characteristics|
99
- # Ensure both the origin and destination zip codes are in our database
100
- if characteristics[:origin_zip_code] and characteristics[:destination_zip_code]
101
- if characteristics[:origin_zip_code] == characteristics[:destination_zip_code]
102
- # FIXME TODO
103
- # Special calculation to deal with travel within the same zipcode
104
- 0
105
- # Ensure we have the lat/lng for both origin and destination
106
- elsif characteristics[:origin_zip_code].latitude and characteristics[:origin_zip_code].longitude and
107
- characteristics[:destination_zip_code].latitude and characteristics[:destination_zip_code].longitude
108
- characteristics[:origin_zip_code].distance_to(characteristics[:destination_zip_code], :units => :kms)
109
- # FIXME TODO: calculate the distance via road using map directions
110
- else
111
- nil
112
- end
100
+ quorum 'from same locality', :needs => [:origin_zip_code, :destination_zip_code] do |characteristics|
101
+ if characteristics[:origin_zip_code] == characteristics[:destination_zip_code]
102
+ 0
113
103
  else
114
104
  nil
115
105
  end
116
106
  end
117
- end
118
-
119
- committee :segment_count do
120
- quorum 'default' do
121
- # ASSUMED based on the fact that FedEx has a hub-spoke system with central and regional distribution centers so seems reasonable for average package to go through four FedEx facilities
122
- 5
107
+ quorum 'from mapquest', :needs => [:origin_zip_code, :destination_zip_code, :mode, :mapquest_api_key] do |characteristics|
108
+ unless characteristics[:mode].name == 'air'
109
+ mapquest = MapQuestDirections.new characteristics[:origin_zip_code].name,
110
+ characteristics[:destination_zip_code].name,
111
+ characteristics[:mapquest_api_key]
112
+ mapquest.distance_in_miles
113
+ end
114
+ end
115
+ quorum 'from direct path', :needs => [:origin_zip_code, :destination_zip_code, :mode] do |characteristics|
116
+ characteristics[:origin_zip_code].distance_to(characteristics[:destination_zip_code], :units => :kms)
123
117
  end
124
118
  end
125
-
119
+
126
120
  committee :mode do
127
121
  quorum 'default' do
128
122
  ShipmentMode.fallback
@@ -148,6 +142,12 @@ module BrighterPlanet
148
142
  3.4
149
143
  end
150
144
  end
145
+
146
+ committee :mapquest_api_key do
147
+ quorum 'default' do
148
+ ENV['MAPQUEST_API_KEY']
149
+ end
150
+ end
151
151
  end
152
152
  end
153
153
  end
@@ -0,0 +1 @@
1
+ require 'mapquest_directions'
@@ -0,0 +1,51 @@
1
+ require 'net/http'
2
+ require 'nokogiri'
3
+
4
+ class MapQuestDirections
5
+ class Failure < StandardError; end
6
+
7
+ attr_accessor :location_1, :location_2, :xml_call, :doc
8
+
9
+ def initialize(location_1, location_2, api_key)
10
+ @base_url = "http://www.mapquestapi.com/directions/v1/route?key=#{api_key}&outFormat=xml&"
11
+ @location_1 = location_1
12
+ @location_2 = location_2
13
+ options = "from=#{transcribe(@location_1)}&to=#{transcribe(@location_2)}"
14
+ @xml_call = @base_url + options
15
+ end
16
+
17
+ def doc
18
+ @doc ||= begin
19
+ xml = get_url(xml_call)
20
+ noko = Nokogiri::XML(xml)
21
+ status = noko.css("statusCode").text
22
+ unless status == '0'
23
+ errors = noko.css("message").map(&:text).join(', ')
24
+ raise Failure, "Could not calculate directions between #{location_1} and #{location_2}: #{errors}"
25
+ end
26
+ noko
27
+ end
28
+ end
29
+
30
+ def drive_time_in_minutes
31
+ drive_time = doc.css("time").first.text
32
+ convert_to_minutes(drive_time)
33
+ end
34
+
35
+ def distance_in_miles
36
+ distance_in_miles = doc.css("distance").first.text.to_i
37
+ end
38
+
39
+ private
40
+ def convert_to_minutes(text)
41
+ (text.to_i / 60).ceil
42
+ end
43
+
44
+ def transcribe(location)
45
+ location.gsub(" ", "+")
46
+ end
47
+
48
+ def get_url(url)
49
+ Net::HTTP.get(::URI.parse(url))
50
+ end
51
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ context "Mapquest API" do
4
+ before(:all) do
5
+ @origin = "816 Meridian St., 37207"
6
+ @destination = "1963 Canary Dr., 36830"
7
+ end
8
+
9
+ context "API working" do
10
+
11
+ describe "directions work" do
12
+
13
+ before(:each) do
14
+ MapQuestDirections.any_instance.stubs(:xml).returns(File.read("spec/lib/mapquest_directions.xml"))
15
+ @directions = MapQuestDirections.new(@origin, @destination)
16
+ end
17
+
18
+ it "should return distance in miles" do
19
+ @directions.distance_in_miles.should == 310
20
+ end
21
+
22
+ it "should return drive time in minutes" do
23
+ @directions.drive_time_in_minutes.should == 303
24
+ end
25
+
26
+ it "should have a status code of 0" do
27
+ @directions.status.should == "0"
28
+ end
29
+
30
+ end #describe
31
+ end # API working
32
+
33
+ context "API not working" do
34
+
35
+ describe "Geocode distance estimation work" do
36
+
37
+ before(:each) do
38
+ MapQuestDirections.any_instance.stubs(:xml).returns(File.read("spec/lib/mapquest_directions_fail.xml"))
39
+ @directions = MapQuestDirections.new(@origin, @destination)
40
+ end
41
+
42
+ it "should return distance in miles" do
43
+ @directions.distance_in_miles.should == 0
44
+ end
45
+
46
+ it "should return drive time in minutes" do
47
+ @directions.drive_time_in_minutes.should == 0
48
+ end
49
+
50
+ it "should have a status code other than 0" do
51
+ @directions.status.should_not == "0"
52
+ end
53
+
54
+ end #describe
55
+ end # API not working
56
+
57
+
58
+ end
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'mocha'
3
+ require 'ruby-debug'
4
+
5
+ $:.unshift File.expand_path('../lib', __FILE__)
6
+ require 'mapquest_directions'
7
+
8
+ MAPQUEST_KEY = "replace_me"
9
+ # http://developer.mapquest.com/
10
+
11
+ Spec::Runner.configure do |config|
12
+ config.mock_with :mocha
13
+ end
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shipment
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
5
4
  prerelease: false
6
5
  segments:
7
6
  - 0
8
7
  - 0
9
- - 3
10
- version: 0.0.3
8
+ - 4
9
+ version: 0.0.4
11
10
  platform: ruby
12
11
  authors:
13
12
  - Andy Rossmeissl
@@ -19,102 +18,96 @@ autorequire:
19
18
  bindir: bin
20
19
  cert_chain: []
21
20
 
22
- date: 2010-12-12 00:00:00 -05:00
21
+ date: 2010-12-14 00:00:00 -05:00
23
22
  default_executable:
24
23
  dependencies:
25
24
  - !ruby/object:Gem::Dependency
26
25
  name: activerecord
27
- prerelease: false
28
26
  requirement: &id001 !ruby/object:Gem::Requirement
29
27
  none: false
30
28
  requirements:
31
29
  - - ~>
32
30
  - !ruby/object:Gem::Version
33
- hash: 5
34
31
  segments:
35
32
  - 3
36
33
  - 0
37
34
  - 1
38
35
  version: 3.0.1
39
36
  type: :development
37
+ prerelease: false
40
38
  version_requirements: *id001
41
39
  - !ruby/object:Gem::Dependency
42
40
  name: bundler
43
- prerelease: false
44
41
  requirement: &id002 !ruby/object:Gem::Requirement
45
42
  none: false
46
43
  requirements:
47
44
  - - ~>
48
45
  - !ruby/object:Gem::Version
49
- hash: 23
50
46
  segments:
51
47
  - 1
52
48
  - 0
53
49
  - 0
54
50
  version: 1.0.0
55
51
  type: :development
52
+ prerelease: false
56
53
  version_requirements: *id002
57
54
  - !ruby/object:Gem::Dependency
58
55
  name: rake
59
- prerelease: false
60
56
  requirement: &id003 !ruby/object:Gem::Requirement
61
57
  none: false
62
58
  requirements:
63
59
  - - ">="
64
60
  - !ruby/object:Gem::Version
65
- hash: 3
66
61
  segments:
67
62
  - 0
68
63
  version: "0"
69
64
  type: :development
65
+ prerelease: false
70
66
  version_requirements: *id003
71
67
  - !ruby/object:Gem::Dependency
72
68
  name: sniff
73
- prerelease: false
74
69
  requirement: &id004 !ruby/object:Gem::Requirement
75
70
  none: false
76
71
  requirements:
77
72
  - - ~>
78
73
  - !ruby/object:Gem::Version
79
- hash: 9
80
74
  segments:
81
75
  - 0
82
76
  - 4
83
77
  - 3
84
78
  version: 0.4.3
85
79
  type: :development
80
+ prerelease: false
86
81
  version_requirements: *id004
87
82
  - !ruby/object:Gem::Dependency
88
83
  name: emitter
89
- prerelease: false
90
84
  requirement: &id005 !ruby/object:Gem::Requirement
91
85
  none: false
92
86
  requirements:
93
87
  - - ~>
94
88
  - !ruby/object:Gem::Version
95
- hash: 19
96
89
  segments:
97
90
  - 0
98
91
  - 3
99
92
  - 0
100
93
  version: 0.3.0
101
94
  type: :runtime
95
+ prerelease: false
102
96
  version_requirements: *id005
103
97
  - !ruby/object:Gem::Dependency
104
98
  name: earth
105
- prerelease: false
106
99
  requirement: &id006 !ruby/object:Gem::Requirement
107
100
  none: false
108
101
  requirements:
109
102
  - - ~>
110
103
  - !ruby/object:Gem::Version
111
- hash: 3
112
104
  segments:
113
105
  - 0
114
106
  - 3
115
107
  - 8
116
108
  version: 0.3.8
117
109
  type: :runtime
110
+ prerelease: false
118
111
  version_requirements: *id006
119
112
  description: A software model in Ruby for the greenhouse gas emissions of a shipment
120
113
  email: andy@rossmeissl.net
@@ -137,8 +130,13 @@ files:
137
130
  - lib/shipment/summarization.rb
138
131
  - lib/test_support/shipment_record.rb
139
132
  - features/support/env.rb
133
+ - features/step_definitions/shipment_steps.rb
140
134
  - features/shipment_committees.feature
141
135
  - features/shipment_emissions.feature
136
+ - vendor/plugin/mapquest/spec/lib/mapquest_directions_spec.rb
137
+ - vendor/plugin/mapquest/spec/spec_helper.rb
138
+ - vendor/plugin/mapquest/init.rb
139
+ - vendor/plugin/mapquest/lib/mapquest_directions.rb
142
140
  has_rdoc: true
143
141
  homepage: http://brighterplanet.github.com/shipment
144
142
  licenses: []
@@ -153,7 +151,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
153
151
  requirements:
154
152
  - - ">="
155
153
  - !ruby/object:Gem::Version
156
- hash: 3
154
+ hash: 774422413
157
155
  segments:
158
156
  - 0
159
157
  version: "0"
@@ -162,7 +160,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
162
160
  requirements:
163
161
  - - ">="
164
162
  - !ruby/object:Gem::Version
165
- hash: 3
166
163
  segments:
167
164
  - 0
168
165
  version: "0"
@@ -175,6 +172,11 @@ specification_version: 3
175
172
  summary: A carbon model for a shipment
176
173
  test_files:
177
174
  - features/support/env.rb
175
+ - features/step_definitions/shipment_steps.rb
178
176
  - features/shipment_committees.feature
179
177
  - features/shipment_emissions.feature
180
178
  - lib/test_support/shipment_record.rb
179
+ - vendor/plugin/mapquest/spec/lib/mapquest_directions_spec.rb
180
+ - vendor/plugin/mapquest/spec/spec_helper.rb
181
+ - vendor/plugin/mapquest/init.rb
182
+ - vendor/plugin/mapquest/lib/mapquest_directions.rb