bizratr 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.
@@ -0,0 +1,102 @@
1
+ module BizRatr
2
+ class Business
3
+ attr_accessor :name, :phone, :address, :state, :country, :zip, :twitter, :ids, :checkins, :users, :likes, :ratings, :review_counts, :coords, :city
4
+
5
+ def initialize(lat, lon, name)
6
+ @ids = {}
7
+ @checkins = {}
8
+ @users = {}
9
+ @likes = {}
10
+ @ratings = {}
11
+ @review_counts = {}
12
+ @coords = [lat, lon]
13
+ @name = name
14
+ end
15
+
16
+ def to_s
17
+ attrs = [:name, :phone, :address, :state, :country, :zip, :twitter, :ids, :checkins, :users, :likes, :ratings, :review_counts, :coords, :city]
18
+ args = attrs.map { |k| "#{k.to_s}=#{send(k)}" }.join(", ")
19
+ "<Business [#{args}]>"
20
+ end
21
+
22
+ def rating
23
+ @ratings.values.inject(:+) / @ratings.length
24
+ end
25
+
26
+ def merge(other)
27
+ @ids = @ids.merge(other.ids)
28
+ @phone ||= other.phone
29
+ @address ||= other.address
30
+ @state ||= other.state
31
+ @country ||= other.country
32
+ @zip ||= other.zip
33
+ @twitter ||= other.twitter
34
+ @city ||= other.city
35
+ @checkins = @checkins.merge(other.checkins)
36
+ @users = @users.merge(other.users)
37
+ @likes = @likes.merge(other.likes)
38
+ @ratings = @ratings.merge(other.ratings)
39
+ @review_counts = @review_counts.merge(other.review_counts)
40
+ @coords[0] = (@coords[0].to_f + other.coords[0].to_f) / 2.0
41
+ @coords[1] = (@coords[1].to_f + other.coords[1].to_f) / 2.0
42
+ return self
43
+ end
44
+
45
+ def add_id(connector, id)
46
+ @ids[connector] = id
47
+ end
48
+
49
+ def add_checkins(connector, checkins)
50
+ @checkins[connector] = checkins
51
+ end
52
+
53
+ def add_users(connector, users)
54
+ @users[connector] = users
55
+ end
56
+
57
+ def add_likes(connector, likes)
58
+ @users[connector] = likes
59
+ end
60
+
61
+ def add_rating(connector, rating)
62
+ @ratings[connector] = rating
63
+ end
64
+
65
+ def add_review_counts(connector, counts)
66
+ @review_counts[connector] = counts
67
+ end
68
+
69
+ def any_equal_ids?(other)
70
+ @ids.each { |k,v|
71
+ return true if other.ids[k] == v
72
+ }
73
+ false
74
+ end
75
+
76
+ def distance_to(other)
77
+ rpd = 0.017453293 # PI/180
78
+ dlat = other.coords[0] - @coords[0]
79
+ dlon = other.coords[1] - @coords[1]
80
+ dlon_rad = dlon * rpd
81
+ dlat_rad = dlat * rpd
82
+ lat1_rad = @coords[0] * rpd
83
+ lon1_rad = @coords[1] * rpd
84
+ lat2_rad = other.coords[0] * rpd
85
+ lon2_rad = other.coords[1] * rpd
86
+ a = (Math.sin(dlat_rad/2))**2 + Math.cos(lat1_rad) * Math.cos(lat2_rad) * (Math.sin(dlon_rad/2))**2
87
+ 2 * Math.atan2( Math.sqrt(a), Math.sqrt(1-a)) * 3956 # 3956 is the radius of the great circle in miles
88
+ end
89
+
90
+ def name_distance_to(other)
91
+ Levenshtein::normalized_distance(@name, other.name)
92
+ end
93
+
94
+ def ==(other)
95
+ return false if other.nil?
96
+ return true if any_equal_ids?(other)
97
+ return true if @phone == other.phone and not @phone.nil?
98
+ return true if distance_to(other) < 0.3 and name_distance_to(other) < 0.4
99
+ false
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,144 @@
1
+ require 'foursquare2'
2
+ require 'yelpster'
3
+ require 'levenshtein'
4
+ require 'google_places'
5
+ require 'geocoder'
6
+
7
+ module BizRatr
8
+ class Connector
9
+ def search_location(location, query)
10
+ raise "Not implemented"
11
+ end
12
+
13
+ def geocode(address)
14
+ Geocoder.coordinates(address)
15
+ end
16
+ end
17
+
18
+ class GooglePlacesConnector < Connector
19
+ def initialize(config)
20
+ @client = GooglePlaces::Client.new(config[:key])
21
+ end
22
+
23
+ def search_location(location, query)
24
+ location = geocode(location) if location.is_a? String
25
+ results = @client.spots(location[0], location[1], :name => query)
26
+ results.map { |item| make_business(item) }
27
+ end
28
+
29
+ def make_business(item)
30
+ b = Business.new(item.lat, item.lng, item.name)
31
+ b.add_id(:google_places, item.id)
32
+ b.phone = item.formatted_phone_number
33
+ b.city = item.city || item.vicinity.split(',').last
34
+ b.country = item.country
35
+ b.zip = item.postal_code
36
+ b.address = item.vicinity.split(',').first
37
+ b.add_rating(:google_places, item.rating)
38
+ b
39
+ end
40
+ end
41
+
42
+ class FourSquareConnector < Connector
43
+ def initialize(config)
44
+ @client = Foursquare2::Client.new(config)
45
+ end
46
+
47
+ def search_location(location, query)
48
+ if location.is_a? Array
49
+ results = @client.search_venues(:ll => location.join(","), :query => query)
50
+ else
51
+ results = @client.search_venues(:near => location, :query => query)
52
+ end
53
+ results['groups'].first['items'].map { |item| make_business(item) }
54
+ end
55
+
56
+ def make_business(item)
57
+ b = Business.new(item['location']['lat'], item['location']['lng'], item['name'])
58
+ b.add_id(:foursquare, item['id'])
59
+ b.phone = item['contact'].fetch('phone', nil)
60
+ b.twitter = item['contact'].fetch('twitter', nil)
61
+ b.state = item['location']['state']
62
+ b.city = item['location']['city']
63
+ b.country = item['location']['cc']
64
+ b.address = item['location']['address']
65
+ b.add_checkins(:foursquare, item['stats']['checkinsCount'])
66
+ b.add_users(:foursquare, item['stats']['usersCount'])
67
+ b
68
+ end
69
+ end
70
+
71
+ class YelpConnector < Connector
72
+ def initialize(config)
73
+ @config = config
74
+ @client = Yelp::Client.new
75
+ end
76
+
77
+ def search_location(location, query)
78
+ # while yelp does support searching by address, it does so much more shittily w/o lat/lon
79
+ location = geocode(location) if location.is_a? String
80
+ config = { :term => query, :latitude => location.first, :longitude => location.last }
81
+ result = @client.search Yelp::V2::Search::Request::GeoPoint.new(config.merge(@config))
82
+ result['businesses'].map { |item|
83
+ make_business(item)
84
+ }
85
+ end
86
+
87
+ def make_business(item)
88
+ b = Business.new(item['location']['coordinate']['latitude'], item['location']['coordinate']['longitude'], item['name'])
89
+ b.add_id(:yelp, item['id'])
90
+ b.state = item['location']['state_code']
91
+ b.zip = item['location']['postal_code']
92
+ b.country = item['location']['country_code']
93
+ b.city = item['location']['city']
94
+ b.address = item['location']['address'].first
95
+ b.phone = item['phone']
96
+ b.add_rating(:yelp, item['rating'])
97
+ b.add_review_counts(:yelp, item['review_count'])
98
+ b
99
+ end
100
+ end
101
+
102
+ class Finder
103
+ def initialize(config)
104
+ @connectors = config.map { |key, value|
105
+ case key
106
+ when :foursquare then FourSquareConnector.new(value)
107
+ when :yelp then YelpConnector.new(value)
108
+ when :google_places then GooglePlacesConnector.new(value)
109
+ else raise "No such connector found: #{key}"
110
+ end
111
+ }
112
+ end
113
+
114
+ # Search for a business (or business category) near lat/lon coordinates. The
115
+ # location parameter should be either an address string or an array consisting of
116
+ # [ lat, lon ].
117
+ def search_location(location, query)
118
+ merge @connectors.map { |c|
119
+ c.search_location(location, query)
120
+ }
121
+ end
122
+
123
+ private
124
+ def merge(lists)
125
+ lists.inject([]) { |o,t| merge_lists(o, t) }
126
+ end
127
+
128
+ def merge_lists(one, two)
129
+ one.map { |first|
130
+ result = first
131
+ two.map! { |second|
132
+ if first == second
133
+ result = first.merge(second)
134
+ nil
135
+ else
136
+ second
137
+ end
138
+ }
139
+ result
140
+ } + two.select { |s| not s.nil? }
141
+ end
142
+ end
143
+
144
+ end
@@ -0,0 +1,3 @@
1
+ module BizRatr
2
+ VERSION = "0.0.1"
3
+ end
data/lib/bizratr.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'bizratr/business'
2
+ require 'bizratr/connector'
3
+ require 'bizratr/version'
4
+
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bizratr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Brian Muller
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-17 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: foursquare2
16
+ requirement: &70329591509040 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70329591509040
25
+ - !ruby/object:Gem::Dependency
26
+ name: yelpster
27
+ requirement: &70329591508340 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70329591508340
36
+ - !ruby/object:Gem::Dependency
37
+ name: google_places
38
+ requirement: &70329591507700 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70329591507700
47
+ - !ruby/object:Gem::Dependency
48
+ name: levenshtein
49
+ requirement: &70329591507040 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70329591507040
58
+ - !ruby/object:Gem::Dependency
59
+ name: google_places
60
+ requirement: &70329591506420 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *70329591506420
69
+ - !ruby/object:Gem::Dependency
70
+ name: geocoder
71
+ requirement: &70329591505820 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: *70329591505820
80
+ description: Get business ratings.
81
+ email: bamuller@gmail.com
82
+ executables: []
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - lib/bizratr/business.rb
87
+ - lib/bizratr/connector.rb
88
+ - lib/bizratr/version.rb
89
+ - lib/bizratr.rb
90
+ - Gemfile
91
+ - Gemfile.lock
92
+ - LICENSE
93
+ - Rakefile
94
+ - README.rdoc
95
+ - docs/BizRatr/Business.html
96
+ - docs/BizRatr/Connector.html
97
+ - docs/BizRatr/Finder.html
98
+ - docs/BizRatr/FourSquareConnector.html
99
+ - docs/BizRatr/GooglePlacesConnector.html
100
+ - docs/BizRatr/YelpConnector.html
101
+ - docs/BizRatr.html
102
+ - docs/created.rid
103
+ - docs/index.html
104
+ - docs/lib/bizratr/business_rb.html
105
+ - docs/lib/bizratr/connector_rb.html
106
+ - docs/lib/bizratr/version_rb.html
107
+ - docs/lib/bizratr_rb.html
108
+ - docs/rdoc.css
109
+ - docs/README_rdoc.html
110
+ homepage: https://github.com/bmuller/bizratr
111
+ licenses: []
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ! '>='
120
+ - !ruby/object:Gem::Version
121
+ version: 1.9.0
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ none: false
124
+ requirements:
125
+ - - ! '>='
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ requirements: []
129
+ rubyforge_project: bizratr
130
+ rubygems_version: 1.8.17
131
+ signing_key:
132
+ specification_version: 3
133
+ summary: Get business ratings.
134
+ test_files: []