moto_recall 0.0.21
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 +7 -0
- data/.gitignore +23 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +68 -0
- data/Rakefile +46 -0
- data/data/class_map.yml +41 -0
- data/data/sample_queries.yml +17 -0
- data/lib/moto_recall.rb +38 -0
- data/lib/moto_recall/class_map.rb +24 -0
- data/lib/moto_recall/client/acura.rb +32 -0
- data/lib/moto_recall/client/audi.rb +29 -0
- data/lib/moto_recall/client/bmw.rb +29 -0
- data/lib/moto_recall/client/chrysler.rb +51 -0
- data/lib/moto_recall/client/ford.rb +49 -0
- data/lib/moto_recall/client/general_motors.rb +40 -0
- data/lib/moto_recall/client/generic_client.rb +44 -0
- data/lib/moto_recall/client/honda.rb +32 -0
- data/lib/moto_recall/client/hyundai.rb +46 -0
- data/lib/moto_recall/client/infiniti.rb +29 -0
- data/lib/moto_recall/client/isuzu.rb +52 -0
- data/lib/moto_recall/client/jaguar.rb +29 -0
- data/lib/moto_recall/client/kia.rb +30 -0
- data/lib/moto_recall/client/mazda.rb +26 -0
- data/lib/moto_recall/client/mitsubishi.rb +46 -0
- data/lib/moto_recall/client/nissan.rb +29 -0
- data/lib/moto_recall/client/subaru.rb +29 -0
- data/lib/moto_recall/client/volkswagen.rb +29 -0
- data/lib/moto_recall/client/volvo.rb +78 -0
- data/lib/moto_recall/recall.rb +40 -0
- data/lib/moto_recall/recall_set.rb +22 -0
- data/lib/moto_recall/sampler.rb +18 -0
- data/lib/moto_recall/sampler/runner.rb +22 -0
- data/lib/moto_recall/sampler/sample.rb +30 -0
- data/lib/moto_recall/sampler/util.rb +40 -0
- data/lib/moto_recall/version.rb +3 -0
- data/moto_recall.gemspec +31 -0
- data/samples-example.csv +191 -0
- data/spec/moto_recall/class_map_spec.rb +24 -0
- data/spec/moto_recall/recall_set_spec.rb +27 -0
- data/spec/moto_recall/recall_spec.rb +24 -0
- data/spec/moto_recall_spec.rb +28 -0
- data/spec/spec_helper.rb +20 -0
- metadata +230 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
require "nokogiri"
|
2
|
+
|
3
|
+
module MotoRecall::Client
|
4
|
+
class Ford < GenericClient
|
5
|
+
def self.url(vin = nil)
|
6
|
+
"https://owner.ford.com/sharedServices/recalls/query.do?country=USA&language=EN&vin=#{vin}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def process(response)
|
10
|
+
parsed_response = JSON.parse(response)
|
11
|
+
recalls = parsed_response["recalls"] || {}
|
12
|
+
([recalls["nhtsa_recall_item"]] + [recalls["fsa_item"]]).flatten.compact
|
13
|
+
end
|
14
|
+
|
15
|
+
def format(recall)
|
16
|
+
# ford returns two different kinds of recalls, each with
|
17
|
+
# different attributes. if the recall has a key called
|
18
|
+
# "local_fsa_r" then it is a fd specific recall and we
|
19
|
+
# need to treat it slightly differently.
|
20
|
+
if recall.has_key?("local_fsa_r")
|
21
|
+
{
|
22
|
+
type: nil,
|
23
|
+
nhtsa_number: nil,
|
24
|
+
oem_number: recall["local_fsa_r"],
|
25
|
+
date: nil,
|
26
|
+
title: recall["description_eng"],
|
27
|
+
description: nil,
|
28
|
+
safety_risk: nil,
|
29
|
+
remedy: nil,
|
30
|
+
status: nil,
|
31
|
+
notes: nil
|
32
|
+
}
|
33
|
+
else
|
34
|
+
{
|
35
|
+
type: nil,
|
36
|
+
nhtsa_number: recall["nhtsa_recall_number"],
|
37
|
+
oem_number: recall["mfr_recall_number"],
|
38
|
+
date: recall["recall_date"],
|
39
|
+
title: recall["description_eng"],
|
40
|
+
description: recall["recall_description"],
|
41
|
+
safety_risk: recall["safety_risk_description"],
|
42
|
+
remedy: recall["remedy_description"],
|
43
|
+
status: recall["mfr_recall_status"],
|
44
|
+
notes: recall["mfr_notes"]
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "mechanize"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module MotoRecall::Client
|
5
|
+
class GeneralMotors < GenericClient
|
6
|
+
def self.url(vin)
|
7
|
+
"https://my.gm.com/services/#{vin}/recalls"
|
8
|
+
end
|
9
|
+
|
10
|
+
def fetch(vin)
|
11
|
+
agent = Mechanize.new
|
12
|
+
response = agent.get(url(vin))
|
13
|
+
response.body
|
14
|
+
end
|
15
|
+
|
16
|
+
def process(response)
|
17
|
+
parsed_response = JSON.parse(response)
|
18
|
+
if parsed_response["data"] && parsed_response["data"]["recalls"]
|
19
|
+
parsed_response["data"]["recalls"]
|
20
|
+
else
|
21
|
+
[]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def format(recall)
|
26
|
+
{
|
27
|
+
type: recall["recall_type"],
|
28
|
+
nhtsa_number: recall["nhtsa_recall_number"],
|
29
|
+
oem_number: recall["mfr_recall_number"],
|
30
|
+
date: recall["recall_date"],
|
31
|
+
title: recall["recall_title"],
|
32
|
+
description: recall["recall_description"],
|
33
|
+
safety_risk: recall["safety_risk_description"],
|
34
|
+
remedy: recall["remedy_description"],
|
35
|
+
status: recall["mfr_recall_status"],
|
36
|
+
notes: nil
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "openssl"
|
2
|
+
|
3
|
+
# don't verify peer ssl certs, not worried about security here
|
4
|
+
# http://stackoverflow.com/questions/33669611/opensslsslsslerror-ssl-connect-returned-1-errno-0-state-error-certificate
|
5
|
+
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
|
6
|
+
I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG = nil
|
7
|
+
|
8
|
+
module MotoRecall::Client
|
9
|
+
class GenericClient
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
end
|
13
|
+
|
14
|
+
def url(vin = nil)
|
15
|
+
self.class.url(vin)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.url(vin = nil)
|
19
|
+
raise NotImplementedError
|
20
|
+
end
|
21
|
+
|
22
|
+
def find(vin)
|
23
|
+
response = fetch(vin)
|
24
|
+
raw_recalls = process(response) || []
|
25
|
+
recalls = raw_recalls.map { |recall| MotoRecall::Recall.new(format(recall)) }
|
26
|
+
MotoRecall::RecallSet.new(recalls, {
|
27
|
+
original_response: response
|
28
|
+
})
|
29
|
+
end
|
30
|
+
|
31
|
+
def fetch(vin)
|
32
|
+
url_with_vin = url(vin)
|
33
|
+
Net::HTTP.get(URI(url_with_vin))
|
34
|
+
end
|
35
|
+
|
36
|
+
def format(recall)
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
def process(response)
|
41
|
+
raise NotImplementedError
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
# If you make any changes to this file, make sure to also make them in the
|
4
|
+
# Acura client as they are pretty much the same running on different domains
|
5
|
+
|
6
|
+
module MotoRecall::Client
|
7
|
+
class Honda < GenericClient
|
8
|
+
def self.url(vin)
|
9
|
+
"http://owners.honda.com/Recalls/GetRecallsByVin/#{vin}/true"
|
10
|
+
end
|
11
|
+
|
12
|
+
def process(response)
|
13
|
+
parsed_response = JSON.parse(response)
|
14
|
+
parsed_response["CampaignTypes"][0]["Campaigns"] rescue []
|
15
|
+
end
|
16
|
+
|
17
|
+
def format(recall)
|
18
|
+
{
|
19
|
+
type: recall["AgencyType"],
|
20
|
+
nhtsa_number: recall["RecallNumber"],
|
21
|
+
oem_number: recall["Id"],
|
22
|
+
date: recall["RecallDate"],
|
23
|
+
title: recall["Description"],
|
24
|
+
description: recall["RecallDescription"],
|
25
|
+
safety_risk: recall["SafetyRiskDescription"],
|
26
|
+
remedy: recall["RemedyDescription"],
|
27
|
+
status: recall["RecallStatus"],
|
28
|
+
notes: nil
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "mechanize"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module MotoRecall::Client
|
5
|
+
class Hyundai < GenericClient
|
6
|
+
def self.url(vin = nil)
|
7
|
+
"https://autoservice.hyundaiusa.com/CampaignHome/?VIN=#{vin}"
|
8
|
+
end
|
9
|
+
|
10
|
+
def fetch(vin)
|
11
|
+
agent = Mechanize.new
|
12
|
+
|
13
|
+
data = { txtVIN: vin }
|
14
|
+
|
15
|
+
response = agent.post(url(vin), data)
|
16
|
+
|
17
|
+
response.body
|
18
|
+
end
|
19
|
+
|
20
|
+
def process(response)
|
21
|
+
doc = Nokogiri::HTML(response)
|
22
|
+
table = doc.css("#open-campaign-table")
|
23
|
+
table.css("tr.campaign-individual").map do |tr|
|
24
|
+
tr.css("td").reduce({}) do |memo, td|
|
25
|
+
memo[td.attr("data-label")] = td.text.sub(/\s+:/, "").strip
|
26
|
+
memo
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def format(recall)
|
32
|
+
{
|
33
|
+
type: recall["Campaign Type"],
|
34
|
+
nhtsa_number: nil,
|
35
|
+
oem_number: recall["Campaign#"],
|
36
|
+
date: recall["Campaign Date"],
|
37
|
+
title: nil,
|
38
|
+
description: recall["Campaign Description"],
|
39
|
+
safety_risk: nil,
|
40
|
+
remedy: recall["Customer Action"],
|
41
|
+
status: nil,
|
42
|
+
notes: nil
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module MotoRecall::Client
|
4
|
+
class Infiniti < GenericClient
|
5
|
+
def self.url(vin)
|
6
|
+
"http://www.infinitiusa.com/dealercenter/api/recalls?vin=#{vin}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def process(data)
|
10
|
+
parsed_data = JSON.parse(data)
|
11
|
+
parsed_data["recalls"]
|
12
|
+
end
|
13
|
+
|
14
|
+
def format(recall)
|
15
|
+
{
|
16
|
+
type: recall["typeCode"],
|
17
|
+
nhtsa_number: recall["nhtsaId"],
|
18
|
+
oem_number: recall["nnaId"],
|
19
|
+
date: recall["effectiveDate"],
|
20
|
+
title: nil,
|
21
|
+
description: "#{recall["primaryDescription"]} #{recall["secondaryDescription"]}",
|
22
|
+
safety_risk: recall["riskIfNotRepaired"],
|
23
|
+
remedy: recall["remedyDescription"],
|
24
|
+
status: nil,
|
25
|
+
notes: nil
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module MotoRecall::Client
|
2
|
+
class Isuzu < GenericClient
|
3
|
+
def self.url(vin = nil)
|
4
|
+
"http://www.isuzu.com/index.jsp"
|
5
|
+
end
|
6
|
+
|
7
|
+
def fetch(vin)
|
8
|
+
agent = Mechanize.new
|
9
|
+
agent.user_agent_alias = "Windows Chrome"
|
10
|
+
|
11
|
+
response = agent.post(url, {
|
12
|
+
"vin" => vin
|
13
|
+
})
|
14
|
+
|
15
|
+
response.body
|
16
|
+
end
|
17
|
+
|
18
|
+
def process(response)
|
19
|
+
doc = Nokogiri::HTML(response)
|
20
|
+
table = doc.css("#form table table")
|
21
|
+
if table.empty?
|
22
|
+
[]
|
23
|
+
else
|
24
|
+
table.css("tr").drop(1).reduce([]) do |memo, row|
|
25
|
+
cells = row.css("td")
|
26
|
+
memo << {
|
27
|
+
oem_number: cells[0].text,
|
28
|
+
description: cells[1].text,
|
29
|
+
nhtsa_number: cells[2].text,
|
30
|
+
status: cells[3].text
|
31
|
+
}
|
32
|
+
memo
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def format(recall)
|
38
|
+
{
|
39
|
+
type: nil,
|
40
|
+
nhtsa_number: recall[:nhtsa_number],
|
41
|
+
oem_number: recall[:oem_number],
|
42
|
+
date: nil,
|
43
|
+
title: nil,
|
44
|
+
description: recall[:description],
|
45
|
+
safety_risk: nil,
|
46
|
+
remedy: nil,
|
47
|
+
status: recall[:status],
|
48
|
+
notes: nil
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module MotoRecall::Client
|
4
|
+
class Jaguar < GenericClient
|
5
|
+
def self.url(vin)
|
6
|
+
"http://www.jaguarusa.com/owners/vin-recall.html?view=vinRecallQuery&vin=#{vin}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def process(response)
|
10
|
+
parsed_response = JSON.parse(response)
|
11
|
+
parsed_response.has_key?("results") ? parsed_response["results"] : []
|
12
|
+
end
|
13
|
+
|
14
|
+
def format(recall)
|
15
|
+
{
|
16
|
+
type: nil,
|
17
|
+
nhtsa_number: recall["number"],
|
18
|
+
oem_number: recall["manufacturerRecallNumber"],
|
19
|
+
date: recall["date"],
|
20
|
+
title: nil,
|
21
|
+
description: recall["recallDesc"],
|
22
|
+
safety_risk: recall["safetyDescription"],
|
23
|
+
remedy: recall["repairDesc"],
|
24
|
+
status: recall["status"],
|
25
|
+
notes: nil
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module MotoRecall::Client
|
4
|
+
class Kia < GenericClient
|
5
|
+
def self.url(vin)
|
6
|
+
"http://www.kia.com/us/en/data/owners/recalls/search/#{vin}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def process(response)
|
10
|
+
parsed_response = JSON.parse(response)
|
11
|
+
result = parsed_response["result"]
|
12
|
+
result.has_key?("recallsResult") ? result["recallsResult"]["recalls"] : []
|
13
|
+
end
|
14
|
+
|
15
|
+
def format(recall)
|
16
|
+
{
|
17
|
+
type: nil,
|
18
|
+
nhtsa_number: recall["nhtsa_recall_number"],
|
19
|
+
oem_number: recall["mfr_recall_number"],
|
20
|
+
date: recall["recall_date"],
|
21
|
+
title: nil,
|
22
|
+
description: recall["recall_description"],
|
23
|
+
safety_risk: recall["safety_risk_description"],
|
24
|
+
remedy: recall["remedy_description"],
|
25
|
+
status: recall["mfr_recall_status"],
|
26
|
+
notes: recall["mfr_notes"]
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module MotoRecall::Client
|
2
|
+
class Mazda < GenericClient
|
3
|
+
def self.url(vin)
|
4
|
+
"https://www.mazdausa.com/handlers/recall.ajax?vin=#{vin}"
|
5
|
+
end
|
6
|
+
def process(response)
|
7
|
+
data = JSON.parse(response)
|
8
|
+
data["body"]["nhtsaRecalls"]
|
9
|
+
end
|
10
|
+
|
11
|
+
def format(recall)
|
12
|
+
{
|
13
|
+
type: nil,
|
14
|
+
nhtsa_number: recall["nhtsa"],
|
15
|
+
oem_number: recall["number"],
|
16
|
+
date: recall["date"],
|
17
|
+
title: nil,
|
18
|
+
description: recall["description"],
|
19
|
+
safety_risk: recall["safetyRisk"],
|
20
|
+
remedy: recall["remedy"],
|
21
|
+
status: recall["status"],
|
22
|
+
notes: recall["notes"]
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module MotoRecall::Client
|
4
|
+
class Mitsubishi < GenericClient
|
5
|
+
def self.url(vin = nil)
|
6
|
+
"https://www.mitsubishicars.com/rs/warranty?vin=#{vin}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def process(response)
|
10
|
+
parsed_response = JSON.parse(response)
|
11
|
+
oe_recalls = parsed_response["openedRecalls"].nil? ? [] : parsed_response["openedRecalls"]
|
12
|
+
nhtsa_recalls = parsed_response["nhtsaOpenRecalls"].nil? ? [] : parsed_response["nhtsaOpenRecalls"]
|
13
|
+
oe_recalls + nhtsa_recalls
|
14
|
+
end
|
15
|
+
|
16
|
+
def format(recall)
|
17
|
+
if recall.has_key?("nhtsaRecallNumber")
|
18
|
+
{
|
19
|
+
type: recall["recallType"],
|
20
|
+
nhtsa_number: recall["nhtsaRecallNumber"],
|
21
|
+
oem_number: recall["mfrRecallNumber"],
|
22
|
+
date: recall["recallDate"],
|
23
|
+
title: nil,
|
24
|
+
description: recall["recallDescription"],
|
25
|
+
safety_risk: recall["safetyRiskDescription"],
|
26
|
+
remedy: recall["remedyDescription"],
|
27
|
+
status: recall["mfrRecallStatus"],
|
28
|
+
notes: nil
|
29
|
+
}
|
30
|
+
else
|
31
|
+
{
|
32
|
+
type: nil,
|
33
|
+
nhtsa_number: nil,
|
34
|
+
oem_number: recall["recallNumber"],
|
35
|
+
date: nil,
|
36
|
+
title: nil,
|
37
|
+
description: recall["recallDescription"],
|
38
|
+
safety_risk: nil,
|
39
|
+
remedy: nil,
|
40
|
+
status: recall["recallStatus"],
|
41
|
+
notes: nil
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module MotoRecall::Client
|
4
|
+
class Nissan < GenericClient
|
5
|
+
def self.url(vin)
|
6
|
+
"http://www.nissanusa.com/dealercenter/api/recalls?vin=#{vin}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def process(data)
|
10
|
+
parsed_data = JSON.parse(data)
|
11
|
+
parsed_data["recalls"]
|
12
|
+
end
|
13
|
+
|
14
|
+
def format(recall)
|
15
|
+
{
|
16
|
+
type: recall["typeCode"],
|
17
|
+
nhtsa_number: recall["nhtsaId"],
|
18
|
+
oem_number: recall["nnaId"],
|
19
|
+
date: recall["effectiveDate"],
|
20
|
+
title: nil,
|
21
|
+
description: "#{recall["primaryDescription"]} #{recall["secondaryDescription"]}",
|
22
|
+
safety_risk: recall["riskIfNotRepaired"],
|
23
|
+
remedy: recall["remedyDescription"],
|
24
|
+
status: nil,
|
25
|
+
notes: nil
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|