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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +68 -0
  7. data/Rakefile +46 -0
  8. data/data/class_map.yml +41 -0
  9. data/data/sample_queries.yml +17 -0
  10. data/lib/moto_recall.rb +38 -0
  11. data/lib/moto_recall/class_map.rb +24 -0
  12. data/lib/moto_recall/client/acura.rb +32 -0
  13. data/lib/moto_recall/client/audi.rb +29 -0
  14. data/lib/moto_recall/client/bmw.rb +29 -0
  15. data/lib/moto_recall/client/chrysler.rb +51 -0
  16. data/lib/moto_recall/client/ford.rb +49 -0
  17. data/lib/moto_recall/client/general_motors.rb +40 -0
  18. data/lib/moto_recall/client/generic_client.rb +44 -0
  19. data/lib/moto_recall/client/honda.rb +32 -0
  20. data/lib/moto_recall/client/hyundai.rb +46 -0
  21. data/lib/moto_recall/client/infiniti.rb +29 -0
  22. data/lib/moto_recall/client/isuzu.rb +52 -0
  23. data/lib/moto_recall/client/jaguar.rb +29 -0
  24. data/lib/moto_recall/client/kia.rb +30 -0
  25. data/lib/moto_recall/client/mazda.rb +26 -0
  26. data/lib/moto_recall/client/mitsubishi.rb +46 -0
  27. data/lib/moto_recall/client/nissan.rb +29 -0
  28. data/lib/moto_recall/client/subaru.rb +29 -0
  29. data/lib/moto_recall/client/volkswagen.rb +29 -0
  30. data/lib/moto_recall/client/volvo.rb +78 -0
  31. data/lib/moto_recall/recall.rb +40 -0
  32. data/lib/moto_recall/recall_set.rb +22 -0
  33. data/lib/moto_recall/sampler.rb +18 -0
  34. data/lib/moto_recall/sampler/runner.rb +22 -0
  35. data/lib/moto_recall/sampler/sample.rb +30 -0
  36. data/lib/moto_recall/sampler/util.rb +40 -0
  37. data/lib/moto_recall/version.rb +3 -0
  38. data/moto_recall.gemspec +31 -0
  39. data/samples-example.csv +191 -0
  40. data/spec/moto_recall/class_map_spec.rb +24 -0
  41. data/spec/moto_recall/recall_set_spec.rb +27 -0
  42. data/spec/moto_recall/recall_spec.rb +24 -0
  43. data/spec/moto_recall_spec.rb +28 -0
  44. data/spec/spec_helper.rb +20 -0
  45. 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