stalkr 0.9.0 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/stalkr.rb +7 -4
- data/lib/stalkr/error.rb +16 -0
- data/lib/stalkr/fedex.rb +38 -52
- data/lib/stalkr/ups.rb +42 -35
- data/lib/stalkr/usps.rb +50 -40
- data/test/stalkr/fedex_test.rb +20 -0
- data/test/stalkr/usps_test.rb +6 -0
- metadata +5 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.9.
|
1
|
+
0.9.1
|
data/lib/stalkr.rb
CHANGED
@@ -11,6 +11,8 @@ require 'date'
|
|
11
11
|
|
12
12
|
require 'stalkr/base'
|
13
13
|
require 'stalkr/result'
|
14
|
+
require 'stalkr/error'
|
15
|
+
|
14
16
|
require 'stalkr/ups'
|
15
17
|
require 'stalkr/usps'
|
16
18
|
require 'stalkr/fedex'
|
@@ -23,16 +25,17 @@ if not DateTime.new.public_methods.include? "to_time" then
|
|
23
25
|
t = d.instance_eval do
|
24
26
|
Time.utc(year, mon, mday, hour, min, sec +
|
25
27
|
sec_fraction)
|
26
|
-
end
|
28
|
+
end
|
29
|
+
t.getlocal
|
27
30
|
end
|
28
31
|
end
|
29
32
|
end
|
30
33
|
|
31
34
|
module Stalkr
|
32
35
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
+
def self.shippers
|
37
|
+
return [ Stalkr::UPS, Stalkr::USPS, Stalkr::FEDEX ]
|
38
|
+
end
|
36
39
|
|
37
40
|
def self.track(id)
|
38
41
|
shipper = nil
|
data/lib/stalkr/error.rb
ADDED
data/lib/stalkr/fedex.rb
CHANGED
@@ -6,7 +6,7 @@ module Stalkr
|
|
6
6
|
|
7
7
|
class FEDEX < Base
|
8
8
|
|
9
|
-
self.regex = /\b((96\d\d\d\d\d ?\d\d\d\d|96\d\d) ?\d\d\d\d ?d\d\d\d( ?\d\d\d)?)
|
9
|
+
self.regex = /\b((96\d\d\d\d\d ?\d\d\d\d|96\d\d) ?\d\d\d\d ?d\d\d\d( ?\d\d\d)?)\b|\b(\d{12}|\d{15})\b/i
|
10
10
|
|
11
11
|
def track(id)
|
12
12
|
|
@@ -15,68 +15,54 @@ class FEDEX < Base
|
|
15
15
|
url.gsub!(/%CODE%/, id)
|
16
16
|
html = fetchurl(url)
|
17
17
|
|
18
|
-
|
19
|
-
ret = Result.new(:FEDEX)
|
20
|
-
ret.status = UNKNOWN
|
21
|
-
return ret
|
22
|
-
end
|
18
|
+
begin
|
23
19
|
|
24
|
-
|
20
|
+
if html =~ /invalid._header/ then
|
21
|
+
ret = Result.new(:FEDEX)
|
22
|
+
ret.status = UNKNOWN
|
23
|
+
return ret
|
24
|
+
end
|
25
25
|
|
26
|
-
|
26
|
+
info_scraper = Scraper.define do
|
27
27
|
|
28
|
-
|
29
|
-
process "div.detailshipdates > div.fielddata", :dates => :text
|
30
|
-
process "div.detaildestination > div.fielddata", :destination => :text
|
31
|
-
process_first "div.detailshipfacts div.dataentry", :service_type => :text
|
28
|
+
array :dates
|
32
29
|
|
33
|
-
|
30
|
+
process "div.detailshipmentstatus", :status => :text
|
31
|
+
process "div.detailshipdates > div.fielddata", :dates => :text
|
32
|
+
process "div.detaildestination > div.fielddata", :destination => :text
|
33
|
+
process_first "div.detailshipfacts div.dataentry", :service_type => :text
|
34
34
|
|
35
|
-
|
35
|
+
result :status, :dates, :destination, :service_type
|
36
36
|
|
37
|
-
|
37
|
+
end
|
38
38
|
|
39
|
-
|
39
|
+
ret = Result.new(:FEDEX)
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
info = info_scraper.scrape(html)
|
42
|
+
|
43
|
+
if info.status.strip.downcase == "delivered" then
|
44
|
+
ret.status = DELIVERED
|
45
|
+
end
|
46
|
+
ret.location = info.destination.strip
|
47
|
+
|
48
|
+
# obj.service_type = info.service_type.strip
|
49
|
+
|
50
|
+
# try to get dates
|
51
|
+
shipped_date = info.dates[0].strip
|
52
|
+
delivery_date = info.dates[1].strip if info.dates.length == 2
|
53
|
+
if shipped_date.empty? then
|
54
|
+
shipped_date = DateTime.strptime( $1, "%b %d, %Y" ).to_time if html =~ /var shipDate = "(.*?);$"/
|
55
|
+
delivery_date = DateTime.strptime( "#{$1} -5", "%b %d, %Y %I:%M %p %z" ).to_time if html =~ /var deliveryDateTime = "(.*?)";$/
|
56
|
+
end
|
57
|
+
ret.delivered_at = delivery_date
|
58
|
+
ret.updated_at = delivery_date
|
59
|
+
|
60
|
+
return ret
|
45
61
|
|
46
|
-
|
62
|
+
rescue Exception => ex
|
63
|
+
raise Stalkr::Error.new(ex, html)
|
47
64
|
|
48
|
-
# try to get dates
|
49
|
-
shipped_date = info.dates[0].strip
|
50
|
-
delivery_date = info.dates[1].strip if info.dates.length == 2
|
51
|
-
if shipped_date.empty? then
|
52
|
-
shipped_date = DateTime.strptime( $1, "%b %d, %Y" ).to_time if html =~ /var shipDate = "(.*?);$"/
|
53
|
-
delivery_date = DateTime.strptime( "#{$1} -5", "%b %d, %Y %I:%M %p %z" ).to_time if html =~ /var deliveryDateTime = "(.*?)";$/
|
54
65
|
end
|
55
|
-
ret.delivered_at = delivery_date
|
56
|
-
ret.updated_at = delivery_date
|
57
|
-
|
58
|
-
# pull progress from JSON
|
59
|
-
# if html =~ /^var detailInfoObject = (.*);$/ then
|
60
|
-
# json = $1
|
61
|
-
# progress = JSON.parse(json)
|
62
|
-
# scans = progress['scans']
|
63
|
-
#
|
64
|
-
# progress = []
|
65
|
-
#
|
66
|
-
# scans.each { |scan|
|
67
|
-
# o = OpenStruct.new
|
68
|
-
# o.location = scan['scanLocation']
|
69
|
-
# o.date = scan['scanDate']
|
70
|
-
# o.local_time = scan['scanTime']
|
71
|
-
# o.desc = scan['scanStatus']
|
72
|
-
# progress << o
|
73
|
-
# }
|
74
|
-
#
|
75
|
-
# obj.progress = progress
|
76
|
-
#
|
77
|
-
# end
|
78
|
-
|
79
|
-
return ret
|
80
66
|
|
81
67
|
end
|
82
68
|
|
data/lib/stalkr/ups.rb
CHANGED
@@ -25,53 +25,60 @@ class UPS < Base
|
|
25
25
|
url.gsub!(/%CODE%/, id)
|
26
26
|
html = fetchurl(url)
|
27
27
|
|
28
|
-
|
28
|
+
begin
|
29
29
|
|
30
|
-
|
31
|
-
array :vals
|
32
|
-
array :lists
|
30
|
+
detail_scraper = Scraper.define do
|
33
31
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
process "#fontControl dd", :vals => :text
|
38
|
-
process "#fontControl ul.clearfix li", :lists => :text
|
32
|
+
array :keys
|
33
|
+
array :vals
|
34
|
+
array :lists
|
39
35
|
|
40
|
-
|
36
|
+
process "#trkNum", :trackingNumber => :text
|
37
|
+
process "#tt_spStatus", :status => :text
|
38
|
+
process "#fontControl dt", :keys => :text
|
39
|
+
process "#fontControl dd", :vals => :text
|
40
|
+
process "#fontControl ul.clearfix li", :lists => :text
|
41
41
|
|
42
|
-
|
42
|
+
result :keys, :vals, :trackingNumber, :status, :lists
|
43
43
|
|
44
|
-
|
44
|
+
end
|
45
45
|
|
46
|
-
|
47
|
-
raise "UPS scraper failed"
|
48
|
-
end
|
46
|
+
details = detail_scraper.scrape(html)
|
49
47
|
|
50
|
-
|
48
|
+
if not details.trackingNumber then
|
49
|
+
raise "UPS scraper failed"
|
50
|
+
end
|
51
51
|
|
52
|
-
|
53
|
-
when "in transit"
|
54
|
-
IN_TRANSIT
|
55
|
-
when "delivered"
|
56
|
-
DELIVERED
|
57
|
-
else
|
58
|
-
UNKNOWN
|
59
|
-
end
|
52
|
+
ret = Result.new(:UPS)
|
60
53
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
54
|
+
ret.status = case details.status.strip.downcase
|
55
|
+
when "in transit"
|
56
|
+
IN_TRANSIT
|
57
|
+
when "delivered"
|
58
|
+
DELIVERED
|
59
|
+
else
|
60
|
+
UNKNOWN
|
61
|
+
end
|
65
62
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
63
|
+
hash = {}
|
64
|
+
details.keys.each_with_index do |k,i|
|
65
|
+
hash[k] = details.vals[i]
|
66
|
+
end
|
70
67
|
|
71
|
-
|
72
|
-
|
68
|
+
if ret.status == DELIVERED then
|
69
|
+
delivered_at = cleanup_html( hash["Delivered On:"] )
|
70
|
+
ret.delivered_at = DateTime.strptime( delivered_at, "%A, %m/%d/%Y at %I:%M %p" ).to_time
|
71
|
+
end
|
73
72
|
|
74
|
-
|
73
|
+
cleanup_html( details.lists[3] ) =~ /Updated: (.*?)$/
|
74
|
+
ret.updated_at = DateTime.strptime( $1, "%m/%d/%Y %I:%M %p" ).to_time
|
75
|
+
|
76
|
+
return ret
|
77
|
+
|
78
|
+
rescue Exception => ex
|
79
|
+
raise Stalkr::Error.new(ex, html)
|
80
|
+
|
81
|
+
end
|
75
82
|
|
76
83
|
end
|
77
84
|
|
data/lib/stalkr/usps.rb
CHANGED
@@ -3,8 +3,10 @@ module Stalkr
|
|
3
3
|
|
4
4
|
class USPS < Base
|
5
5
|
|
6
|
-
# 20 or 22 digits, beginning with 91 (and with or without spaces between groupings)
|
7
|
-
|
6
|
+
# 1) 20 or 22 digits, beginning with 91 (and with or without spaces between groupings)
|
7
|
+
# 2) 20 digits (no real pattern)
|
8
|
+
# TODO: 2nd pattern is too generic. Could also match other carriers.
|
9
|
+
self.regex = /\b(91\d{2} ?\d{4} ?\d{4} ?\d{4} ?\d{4} ?\d{2}|91\d{2} ?\d{4} ?\d{4} ?\d{4} ?\d{4})\b|\b(\d{20})\b/i
|
8
10
|
|
9
11
|
def track(id)
|
10
12
|
|
@@ -16,62 +18,70 @@ class USPS < Base
|
|
16
18
|
url.gsub!(/%CODE%/, id)
|
17
19
|
html = fetchurl(url)
|
18
20
|
|
19
|
-
|
21
|
+
begin
|
20
22
|
|
21
|
-
|
22
|
-
array :info
|
23
|
-
array :txt
|
23
|
+
info_scraper = Scraper.define do
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
array :details
|
26
|
+
array :info
|
27
|
+
array :txt
|
28
28
|
|
29
|
-
|
29
|
+
process "span.mainTextbold", :info => :text
|
30
|
+
process "td.mainTextbold", :details => :text
|
31
|
+
process "td.mainText", :txt => :text
|
30
32
|
|
31
|
-
|
33
|
+
result :details, :info, :txt
|
32
34
|
|
33
|
-
|
35
|
+
end
|
34
36
|
|
35
|
-
|
36
|
-
if scrape.info[0].gsub(/ /, '') != id then
|
37
|
-
raise "USPS scraper failed"
|
38
|
-
end
|
37
|
+
scrape = info_scraper.scrape(html)
|
39
38
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
elsif scrape.info.find{ |i| i.downcase == "delivered" } then
|
45
|
-
ret.status = DELIVERED
|
46
|
-
else
|
47
|
-
ret.status = UNKNOWN
|
48
|
-
end
|
39
|
+
# verify its the correct response page
|
40
|
+
if scrape.info[0].gsub(/ /, '') != id then
|
41
|
+
raise "USPS scraper failed"
|
42
|
+
end
|
49
43
|
|
50
|
-
|
51
|
-
|
52
|
-
|
44
|
+
# extract and return
|
45
|
+
ret = Result.new(:USPS)
|
46
|
+
if scrape.txt.find{ |t| t =~ /There is no record of this item/ } then
|
47
|
+
ret.status = UNKNOWN
|
48
|
+
elsif scrape.info.find{ |i| i.downcase == "delivered" } then
|
49
|
+
ret.status = DELIVERED
|
50
|
+
else
|
51
|
+
ret.status = UNKNOWN
|
52
|
+
end
|
53
|
+
|
54
|
+
if scrape.details and not scrape.details.empty? then
|
55
|
+
if scrape.details[0] =~ /^(.*?), (.*? \d+, \d+), (\d+:\d+ .m), (.*?)$/ then
|
56
|
+
ret.location = $4
|
57
|
+
# not sure if this time is always in EST or the time of the area in which it was delivered?
|
58
|
+
ret.updated_at = DateTime.strptime( "#{$2} #{$3} -0500", "%B %d, %Y %I:%M %p %z" ).to_time
|
59
|
+
if ret.status == DELIVERED then
|
60
|
+
ret.delivered_at = ret.updated_at
|
61
|
+
end
|
62
|
+
|
63
|
+
elsif scrape.details[0] =~ /Electronic Shipping Info Received/ then
|
64
|
+
ret.status = IN_TRANSIT
|
65
|
+
end
|
66
|
+
|
67
|
+
elsif s = scrape.txt.find{ |t| t =~ /files offline/ }
|
68
|
+
s =~ /at (\d+:\d+ .m) on (.*? \d+, \d+) in (.*?)\.$/
|
69
|
+
ret.location = $3
|
53
70
|
# not sure if this time is always in EST or the time of the area in which it was delivered?
|
54
|
-
ret.updated_at = DateTime.strptime( "#{$2} #{$
|
71
|
+
ret.updated_at = DateTime.strptime( "#{$2} #{$1} -0500", "%B %d, %Y %I:%M %p %z" ).to_time
|
55
72
|
if ret.status == DELIVERED then
|
56
73
|
ret.delivered_at = ret.updated_at
|
57
74
|
end
|
58
75
|
|
59
|
-
elsif scrape.details[0] =~ /Electronic Shipping Info Received/ then
|
60
|
-
ret.status = IN_TRANSIT
|
61
76
|
end
|
62
77
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
ret.updated_at = DateTime.strptime( "#{$2} #{$1} -0500", "%B %d, %Y %I:%M %p %z" ).to_time
|
68
|
-
if ret.status == DELIVERED then
|
69
|
-
ret.delivered_at = ret.updated_at
|
70
|
-
end
|
78
|
+
return ret
|
79
|
+
|
80
|
+
rescue Exception => ex
|
81
|
+
raise Stalkr::Error.new(ex, html)
|
71
82
|
|
72
83
|
end
|
73
84
|
|
74
|
-
return ret
|
75
85
|
end
|
76
86
|
|
77
87
|
end # class USPS
|
data/test/stalkr/fedex_test.rb
CHANGED
@@ -18,9 +18,29 @@ class FEDEX_Test < Test::Unit::TestCase
|
|
18
18
|
|
19
19
|
end
|
20
20
|
|
21
|
+
def test_track_bad_code_numeric
|
22
|
+
|
23
|
+
begin
|
24
|
+
id = "200793242831442"
|
25
|
+
info = Stalkr::FEDEX.new.track(id)
|
26
|
+
flunk("no exception raised")
|
27
|
+
|
28
|
+
rescue Exception => ex
|
29
|
+
assert(ex.kind_of? Stalkr::Error)
|
30
|
+
assert(!(ex.html.nil? || ex.html.empty?))
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
21
35
|
def test_extract_id
|
22
36
|
str = "asdf 106050761498748 asdfas"
|
23
37
|
assert(Stalkr::FEDEX.extract_id(str) == "106050761498748")
|
24
38
|
end
|
25
39
|
|
40
|
+
def test_should_not_extract
|
41
|
+
str = "Your tracking number is: 02185456301085740874. "
|
42
|
+
id = Stalkr::FEDEX.extract_id(str)
|
43
|
+
assert(id.nil?)
|
44
|
+
end
|
45
|
+
|
26
46
|
end
|
data/test/stalkr/usps_test.rb
CHANGED
@@ -30,4 +30,10 @@ class USPS_Test < Test::Unit::TestCase
|
|
30
30
|
assert(Stalkr::USPS.extract_id(str) == "9102901001299192824023")
|
31
31
|
end
|
32
32
|
|
33
|
+
def test_extract_id_2
|
34
|
+
str = "Your tracking number is: 02185456301085740874. "
|
35
|
+
id = Stalkr::USPS.extract_id(str)
|
36
|
+
assert(!id.nil? && id == "02185456301085740874")
|
37
|
+
end
|
38
|
+
|
33
39
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stalkr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 57
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 9
|
9
|
-
-
|
10
|
-
version: 0.9.
|
9
|
+
- 1
|
10
|
+
version: 0.9.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Chetan Sarva
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-02-
|
18
|
+
date: 2011-02-16 00:00:00 -05:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -111,6 +111,7 @@ files:
|
|
111
111
|
- VERSION
|
112
112
|
- lib/stalkr.rb
|
113
113
|
- lib/stalkr/base.rb
|
114
|
+
- lib/stalkr/error.rb
|
114
115
|
- lib/stalkr/fedex.rb
|
115
116
|
- lib/stalkr/result.rb
|
116
117
|
- lib/stalkr/ups.rb
|