em-synchrony-dataone-vin 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c877baafea2c7245911ad6694c98d8a623c0fa64
4
+ data.tar.gz: 3a99bf467ca37e2d9779c867708514ad75af8f42
5
+ SHA512:
6
+ metadata.gz: c74f30d047628e390420ac38caa7c3d70b604e3e6ac6c81158ba4fb8ef9af483f6d32279d33453f49aa0dcde2698dd2a7af20eea07c200c6a5f677d17a9abe99
7
+ data.tar.gz: 09a37d24790f4fb8e01fb3bb2e8fcef5f5f5b7bf70e52ff16ab5c26bd48dd4f6ebae55b9a357b135c8edb4436fe1018e1ae12ebb710c525a136dc3cd65e71311
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ dataone-config.rb
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in dataone-vin.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Scott Nielsen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # EM::Synchrony::DataoneVin
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'em-synchrony-dataone-vin'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install em-synchrony-dataone-vin
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs = ['lib', 'test']
5
+ t.pattern = 'test/**/*_test.rb'
6
+ end
7
+
8
+ task :default => :test
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'em-synchrony/dataone-vin/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "em-synchrony-dataone-vin"
8
+ spec.version = EventMachine::Synchrony::DataoneVin::VERSION
9
+ spec.authors = ["Scott Nielsen", "Jake Mallory"]
10
+ spec.email = ["scottnielsen5@gmail.com", "tinomen@gmail.com"]
11
+ spec.description = %q{A client for the Dataone vindecoding service that runs with EM::Synchrony}
12
+ spec.summary = %q{A client for the Dataone vindecoding service that runs with EM::Synchrony}
13
+ spec.homepage = "https://github.com/scizo/em-synchrony-dataone-vin"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "em-http-request", "~> 1.1.0"
22
+ spec.add_dependency "em-synchrony", "~> 1.0.3"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.3"
25
+ spec.add_development_dependency "rake", "~> 0.9.6"
26
+ end
@@ -0,0 +1,7 @@
1
+ module EventMachine
2
+ module Synchrony
3
+ module DataoneVin
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,165 @@
1
+ require 'em-synchrony/dataone-vin'
2
+
3
+ module EventMachine
4
+ module Synchrony
5
+ module DataoneVin
6
+ class VinExploderAdapter
7
+ class Error < ::StandardError; end
8
+
9
+ DRIVELINE_TYPES = {
10
+ '4x4' => '4WD',
11
+ '4X4' => '4WD',
12
+ '4x2' => 'RWD',
13
+ '4X2' => 'RWD',
14
+ }
15
+
16
+ FUEL_TYPES = {
17
+ 'B' => 'BIODIESEL',
18
+ 'D' => 'DIESEL',
19
+ 'F' => 'FFV',
20
+ 'G' => 'GAS',
21
+ 'I' => 'HYBRID',
22
+ 'L' => 'ELECTRIC',
23
+ 'N' => 'NATURALGAS',
24
+ 'P' => 'PETROLEUM',
25
+ 'Y' => 'HYBRID',
26
+ }
27
+
28
+ BRAKE_TYPES = {
29
+ ['Not Available', 'Not Available'] => 'Non-ABS',
30
+ ['Not Available', 'Standard'] => '4-Wheel ABS',
31
+ ['Not Available', 'Optional'] => 'Non-ABS | 4-Wheel ABS',
32
+ ['Standard', 'Not Available'] => '2-Wheel ABS',
33
+ ['Standard', 'Standard'] => '2-Wheel ABS | 4-Wheel ABS',
34
+ ['Standard', 'Optional'] => '2-Wheel ABS | 4-Wheel ABS',
35
+ ['Optional', 'Not Available'] => 'Non-ABS | 2-Wheel ABS',
36
+ ['Optional', 'Standard'] => '2-Wheel ABS | 4-Wheel ABS',
37
+ ['Optional', 'Optional'] => 'Non-ABS | 2-Wheel ABS | 4-Wheel ABS',
38
+ }
39
+
40
+ DATA_PATHS = {
41
+ 'year' => ['basic_data', 'year'],
42
+ 'make' => ['basic_data', 'make'],
43
+ 'model' => ['basic_data', 'model'],
44
+ 'trim_level' => ['basic_data', 'trim'],
45
+ 'engine_type' => ['engines', 0, 'name'],
46
+ 'engine_displacement' => ['engines', 0, 'displacement'],
47
+ 'engine_shape' => ['engines', 0, 'block_type'],
48
+ 'body_style' => ['basic_data', 'body_type'],
49
+ 'manufactured_in' => ['basic_data', 'country_of_manufacture'],
50
+ 'driveline' => ['basic_data', 'drive_type'],
51
+ 'fuel_type' => ['engines', 0, 'fuel_type'],
52
+ 'transmission-long' => ['transmissions', 0, 'name'],
53
+ 'gears' => ['transmissions', 0, 'gears'],
54
+ 'transmission-type' => ['transmissions', 0, 'type'],
55
+ 'tank' => ['specifications', ['category', 'Fuel Tanks'], 'specifications', ['name', 'Fuel Tank 1 Capacity (Gallons)'], 'value'],
56
+ 'abs_two_wheel' => ['safety_equipment', 'abs_two_wheel'],
57
+ 'abs_four_wheel' => ['safety_equipment', 'abs_four_wheel'],
58
+ 'gvwr_class' => ['specifications', ['category', 'Measurements of Weight'], 'specifications', ['name', 'Gross Vehicle Weight Rating'], 'value'],
59
+ 'vehicle_type' => ['basic_data', 'vehicle_type'],
60
+ 'number_of_cylinders' => ['engines', 0, 'cylinders'],
61
+ 'number_of_doors' => ['basic_data', 'doors'],
62
+ 'standard_seating' => ['specifications', ['category', 'Seating'], 'specifications', ['name', 'Standard Seating'], 'value'],
63
+ 'optional_seating' => ['specifications', ['category', 'Seating'], 'specifications', ['name', 'Max Seating'], 'value'],
64
+ 'length' => ['specifications', ['category', 'Measurements of Size and Shape'], 'specifications', ['name', 'Length'], 'value'],
65
+ 'width' => ['specifications', ['category', 'Measurements of Size and Shape'], 'specifications', ['name', 'Width'], 'value'],
66
+ 'height' => ['specifications', ['category', 'Measurements of Size and Shape'], 'specifications', ['name', 'Height'], 'value'],
67
+ 'production_seq_number' => ['This will always be nil'],
68
+ }
69
+
70
+ def initialize(options)
71
+ client_id, authorization_code = options.values_at(:client_id, :authorization_code)
72
+
73
+ unless client_id && authorization_code
74
+ raise Error.new "DataoneVin::VinExploderAdapter requires both a client_id and an authorization_code"
75
+ end
76
+
77
+ DataoneVin.configure client_id, authorization_code
78
+ end
79
+
80
+ def explode(vin)
81
+ format_response DataoneVin.get(vin), vin
82
+ end
83
+
84
+ def format_response(response, vin)
85
+ errors = errors(response)
86
+ return {:errors => errors} unless errors.empty?
87
+
88
+ data = response['query_responses']['Request-Sample']
89
+ explosion(data['common_data']).merge \
90
+ :errors => [],
91
+ :vin => vin,
92
+ :vin_key => vin_key(vin),
93
+ :vendor_result => data
94
+ end
95
+
96
+ def errors(response)
97
+ query_error = response['query_responses']['Request-Sample']['query_error']['error_message']
98
+ errors = response['decoder_messages']['decoder_errors']
99
+ errors << query_error unless query_error.empty?
100
+ errors
101
+ end
102
+
103
+ def explosion(data)
104
+ normalize Hash[DATA_PATHS.map do |(key, path)|
105
+ [key, lookup_data(data, path)]
106
+ end]
107
+ end
108
+
109
+ def lookup_data(data, path)
110
+ path.reduce(data) do |d, (k, v)|
111
+ if v
112
+ d.find{|h| h[k] == v}
113
+ else
114
+ d[k]
115
+ end
116
+ end
117
+ rescue
118
+ nil
119
+ end
120
+
121
+ def normalize(data)
122
+ data['driveline'] = normalize_driveline data['driveline']
123
+ data['fuel_type'] = normalize_fuel_type data['fuel_type']
124
+ data['vehicle_type'] = data['vehicle_type'].upcase
125
+ data['has_turbo'] = !!(data['engine_type'] =~ /turbo/i)
126
+ data['transmission-short'] = "#{data.delete('gears')}#{data.delete('transmission-type')}"
127
+ data['anti-brake_system'] = normalize_brakes data.delete('abs_two_wheel'), data.delete('abs_four_wheel')
128
+ data['gvwr_class'] = normalize_gvwr_class data['gvwr_class']
129
+
130
+ data
131
+ end
132
+
133
+ def normalize_driveline(driveline)
134
+ DRIVELINE_TYPES.fetch(driveline, driveline)
135
+ end
136
+
137
+ def normalize_fuel_type(fuel_type)
138
+ FUEL_TYPES.fetch(fuel_type, 'No data')
139
+ end
140
+
141
+ def normalize_brakes(abs_two_wheel, abs_four_wheel)
142
+ BRAKE_TYPES.fetch([abs_two_wheel, abs_four_wheel], 'No Data')
143
+ end
144
+
145
+ def normalize_gvwr_class(weight_rating)
146
+ case weight_rating.to_i
147
+ when 0..6_000 then '1'
148
+ when 6_001..10_000 then '2'
149
+ when 10_001..14_000 then '3'
150
+ when 14_001..16_000 then '4'
151
+ when 16_001..19_500 then '5'
152
+ when 19_501..26_000 then '6'
153
+ when 26_001..33_000 then '7'
154
+ when 33_001..Float::INFINITY then '8'
155
+ else '1'
156
+ end
157
+ end
158
+
159
+ def vin_key(vin)
160
+ "#{vin[0,8]}#{vin[9,2]}"
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,92 @@
1
+ require 'json'
2
+ require 'em-http'
3
+ require 'em-synchrony'
4
+ require 'em-synchrony/em-http'
5
+ require 'em-synchrony/dataone-vin/version'
6
+
7
+ module EventMachine
8
+ module Synchrony
9
+ module DataoneVin
10
+ module_function
11
+
12
+ def configure(client_id, authorization_code)
13
+ @dataone_config = {
14
+ :client_id => client_id,
15
+ :authorization_code => authorization_code
16
+ }
17
+ end
18
+
19
+ def get(vin)
20
+ JSON::load EM::HttpRequest.new(request_url).post(
21
+ :head => {:content_type => 'application/x-www-form-urlencoded'},
22
+ :body => request_hash(vin)
23
+ ).response
24
+ end
25
+
26
+ def request_url
27
+ "https://www.dataonesoftware.com/webservices/vindecoder/decode"
28
+ end
29
+
30
+ def request_hash(vin)
31
+ @dataone_config.merge :decoder_query => decoder_settings(vin)
32
+ end
33
+
34
+ def decoder_settings(vin)
35
+ JSON::dump \
36
+ "decoder_settings"=>
37
+ {"display"=>"full",
38
+ "version"=>"7.0.0",
39
+ "styles"=>"on",
40
+ "style_data_packs"=>
41
+ {"basic_data"=>"on",
42
+ "pricing"=>"on",
43
+ "engines"=>"on",
44
+ "transmissions"=>"on",
45
+ "specifications"=>"on",
46
+ "optional_equipment"=>"on",
47
+ "colors"=>"on",
48
+ "safety_equipment"=>"on",
49
+ "warranties"=>"on"},
50
+ "common_data"=>"on",
51
+ "common_data_packs"=>
52
+ {"basic_data"=>"on",
53
+ "pricing"=>"on",
54
+ "engines"=>"on",
55
+ "transmissions"=>"on",
56
+ "specifications"=>"on",
57
+ "colors"=>"on",
58
+ "safety_equipment"=>"on",
59
+ "warranties"=>"on"}},
60
+ "query_requests"=>
61
+ {"Request-Sample"=>
62
+ {"vin"=>vin,
63
+ "year"=>"",
64
+ "make"=>"",
65
+ "model"=>"",
66
+ "trim"=>"",
67
+ "model_number"=>"",
68
+ "package_code"=>"",
69
+ "drive_type"=>"",
70
+ "vehicle_type"=>"",
71
+ "body_type"=>"",
72
+ "body_subtype"=>"",
73
+ "doors"=>"",
74
+ "bedlength"=>"",
75
+ "wheelbase"=>"",
76
+ "msrp"=>"",
77
+ "invoice_price"=>"",
78
+ "engine"=>
79
+ {"description"=>"",
80
+ "block_type"=>"",
81
+ "cylinders"=>"",
82
+ "displacement"=>"",
83
+ "fuel_type"=>""},
84
+ "transmission"=>{"description"=>"", "trans_type"=>"", "trans_speeds"=>""},
85
+ "optional_equipment_codes"=>"",
86
+ "installed_equipment_descriptions"=>"",
87
+ "interior_color"=>{"description"=>"", "color_code"=>""},
88
+ "exterior_color"=>{"description"=>"", "color_code"=>""}}}
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,52 @@
1
+ require 'test_helper'
2
+ require 'em-synchrony/dataone-vin/vin_exploder_adapter.rb'
3
+
4
+ module EventMachine
5
+ module Synchrony
6
+ module DataoneVin
7
+ describe VinExploderAdapter do
8
+ include TestHelpers
9
+
10
+ let(:adapter) {
11
+ VinExploderAdapter.new \
12
+ :client_id => DATAONE_CONFIG[0],
13
+ :authorization_code => DATAONE_CONFIG[1]
14
+ }
15
+
16
+ it 'should format the raw dataone json to the desired result' do
17
+ adapter.format_response(expected_result, '1FT7W2BT6BEC91853').must_equal \
18
+ 'year' => '2000',
19
+ 'make' => 'Toyota',
20
+ 'model' => 'Tundra',
21
+ 'trim_level' => 'SR5',
22
+ 'engine_type' => 'ED 4L NA V 8 double overhead cam (DOHC) 32V',
23
+ 'engine_displacement' => '4.7',
24
+ 'engine_shape' => 'V',
25
+ 'body_style' => 'Pickup',
26
+ 'manufactured_in' => '',
27
+ 'driveline' => 'RWD',
28
+ 'fuel_type' => 'GAS',
29
+ 'anti-brake_system' => 'No Data',
30
+ 'gvwr_class' => '2',
31
+ 'transmission-long' => '4-Speed Automatic',
32
+ 'transmission-short' => '4A',
33
+ 'tank' => '26',
34
+ 'vehicle_type' => 'TRUCK',
35
+ 'has_turbo' => false,
36
+ 'number_of_cylinders' => '8',
37
+ 'number_of_doors' => '4',
38
+ 'standard_seating' => '6',
39
+ 'optional_seating' => '6',
40
+ 'length' => '217.5',
41
+ 'width' => '75.2',
42
+ 'height' => '70.5',
43
+ 'production_seq_number' => nil,
44
+ :errors => [],
45
+ :vin => '1FT7W2BT6BEC91853',
46
+ :vin_key => '1FT7W2BTBE',
47
+ :vendor_result => expected_result['query_responses']['Request-Sample']
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,23 @@
1
+ require 'test_helper'
2
+ require 'em-synchrony/dataone-vin'
3
+
4
+ module EventMachine
5
+ module Synchrony
6
+ describe DataoneVin do
7
+ include TestHelpers
8
+
9
+ before do
10
+ DataoneVin.configure(*DATAONE_CONFIG)
11
+ end
12
+
13
+ it 'should work' do
14
+ EM.synchrony do
15
+ result = EM::Synchrony::DataoneVin.get('5TBRT3418YS094830')
16
+ result['query_responses']['Request-Sample'].delete('transaction_id')
17
+ result.must_equal expected_result
18
+ EM.stop
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end