Active 0.0.42 → 0.1.7
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.
- data/.gitignore +6 -0
- data/.irbrc +21 -0
- data/.rspec +1 -0
- data/.rvmrc +2 -0
- data/Active.gemspec +28 -29
- data/Gemfile +4 -0
- data/History.txt +5 -2
- data/{README.txt → README.md} +4 -20
- data/Rakefile +11 -17
- data/lib/Active.rb +11 -79
- data/lib/active/activity.rb +7 -0
- data/lib/active/article.rb +7 -0
- data/lib/active/asset.rb +205 -0
- data/lib/active/errors.rb +9 -0
- data/lib/active/query.rb +225 -0
- data/lib/active/result.rb +7 -0
- data/lib/active/results.rb +6 -0
- data/lib/active/training.rb +7 -0
- data/lib/active/version.rb +3 -0
- data/lib/ext/hash_extensions.rb +8 -0
- data/spec/asset_spec.rb +47 -0
- data/spec/search_spec.rb +383 -432
- data/spec/spec_helper.rb +23 -2
- metadata +113 -116
- data/bin/Active +0 -7
- data/lib/.DS_Store +0 -0
- data/lib/services/.DS_Store +0 -0
- data/lib/services/IActivity.rb +0 -39
- data/lib/services/_ats.rb +0 -215
- data/lib/services/active_works.rb +0 -167
- data/lib/services/activity.rb +0 -512
- data/lib/services/address.rb +0 -17
- data/lib/services/ats.rb +0 -229
- data/lib/services/dto/user.rb +0 -9
- data/lib/services/gsa.rb +0 -205
- data/lib/services/reg_center.rb +0 -270
- data/lib/services/sanitize.rb +0 -108
- data/lib/services/search.rb +0 -494
- data/lib/services/validators.rb +0 -124
- data/rspec-tm +0 -1
- data/rvmrc +0 -1
- data/spec/.DS_Store +0 -0
- data/spec/Active_spec.rb +0 -28
- data/spec/activeworks_spec.rb +0 -60
- data/spec/activity_spec.rb +0 -421
- data/spec/ats_spec.rb +0 -106
- data/spec/benchmark/search_bench.rb +0 -55
- data/spec/custom_matchers_spec.rb +0 -27
- data/spec/gsa_spec.rb +0 -210
- data/spec/reg_spec.rb +0 -173
- data/spec/search_memcached_spec.rb +0 -42
- data/spec/validators_spec.rb +0 -19
- data/test/test_Active.rb +0 -0
- data/version.txt +0 -1
data/lib/services/reg_center.rb
DELETED
@@ -1,270 +0,0 @@
|
|
1
|
-
module Active
|
2
|
-
module Services
|
3
|
-
|
4
|
-
class RegCenterError < StandardError; end
|
5
|
-
|
6
|
-
class RegCenter < IActivity
|
7
|
-
require 'nokogiri'
|
8
|
-
require 'open-uri'
|
9
|
-
require 'digest/sha1'
|
10
|
-
attr_accessor :asset_type_id
|
11
|
-
|
12
|
-
# attr_reader :metadata_loaded
|
13
|
-
# EXAMPLE Data hash
|
14
|
-
# {:asset_id=>"A9EF9D79-F859-4443-A9BB-91E1833DF2D5", :substitution_url=>"1878023", :asset_type_name=>"Active.com Event Registration",
|
15
|
-
# :asset_name=>"Fitness, Pilates Mat Class (16 Yrs. & Up)", :url=>"http://www.active.com/page/Event_Details.htm?event_id=1878023",
|
16
|
-
# :asset_type_id=>"EA4E860A-9DCD-4DAA-A7CA-4A77AD194F65", :xmlns=>"http://api.asset.services.active.com"}
|
17
|
-
|
18
|
-
def initialize(data={})
|
19
|
-
@data = HashWithIndifferentAccess.new(data) || HashWithIndifferentAccess.new
|
20
|
-
@asset_type_id = "EA4E860A-9DCD-4DAA-A7CA-4A77AD194F65"
|
21
|
-
end
|
22
|
-
|
23
|
-
def source
|
24
|
-
:reg_center
|
25
|
-
end
|
26
|
-
|
27
|
-
def title
|
28
|
-
if @data.has_key?("event") && @data["event"].has_key?("eventName")
|
29
|
-
cleanup_reg_string(@data["event"]["eventName"])
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def event_image_url
|
34
|
-
if @data.has_key?("event") && @data["event"].has_key?("eventImageUrl")
|
35
|
-
@data["event"]["eventImageUrl"]
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def url
|
40
|
-
if @data.has_key?("event") && @data["event"].has_key?("eventDetailsPageUrl")
|
41
|
-
@data["event"]["eventDetailsPageUrl"]
|
42
|
-
elsif @data.has_key?("event") && @data["event"].has_key?("registrationUrl")
|
43
|
-
@data["event"]["registrationUrl"]
|
44
|
-
elsif @data.has_key?("event") && @data["event"].has_key?("eventContactUrl")
|
45
|
-
@data["event"]["eventContactUrl"]
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def registration_url
|
50
|
-
if @data.has_key?("event") && @data["event"].has_key?("registrationUrl")
|
51
|
-
@data["event"]["registrationUrl"]
|
52
|
-
else
|
53
|
-
""
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def event_url
|
58
|
-
@data[:event][:eventUrl]
|
59
|
-
end
|
60
|
-
|
61
|
-
def categories
|
62
|
-
if @data.has_key?("event") && @data["event"].has_key?("channels") && @data["event"]["channels"]!=nil && @data["event"]["channels"].has_key?("channel") && @data["event"]["channels"]["channel"]!=nil
|
63
|
-
channels = @data["event"]["channels"]["channel"]
|
64
|
-
if channels.class==Array
|
65
|
-
@data["event"]["channels"]["channel"].collect {|e| e["channelName"]}
|
66
|
-
else
|
67
|
-
#hash
|
68
|
-
[channels["channelName"]]
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def asset_id
|
74
|
-
if @data.has_key?("event") && @data["event"].has_key?("assetID")
|
75
|
-
@data["event"]["assetID"]
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def primary_category
|
80
|
-
if @data["event"]["channels"]["channel"]!=nil
|
81
|
-
channels = @data["event"]["channels"]["channel"]
|
82
|
-
if channels.class==Array
|
83
|
-
channels.each do |c|
|
84
|
-
return c["channelName"] if c.has_key?("primaryChannel") && c["primaryChannel"]=="true"
|
85
|
-
end
|
86
|
-
nil
|
87
|
-
else
|
88
|
-
#hash
|
89
|
-
return channels["channelName"] if channels.has_key?("primaryChannel") && channels["primaryChannel"]=="true"
|
90
|
-
return nil
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def address
|
96
|
-
@address = validated_address({
|
97
|
-
:name => @data["event"]["eventLocation"],
|
98
|
-
:address => @data["event"]["eventAddress"],
|
99
|
-
:city => @data["event"]["eventCity"],
|
100
|
-
:state => @data["event"]["eventState"],
|
101
|
-
:zip => @data["event"]["eventZip"],
|
102
|
-
:lat => @data["event"]["latitude"],
|
103
|
-
:lng => @data["event"]["longitude"],
|
104
|
-
:country => @data["event"]["eventCountry"]
|
105
|
-
} )
|
106
|
-
end
|
107
|
-
|
108
|
-
def start_date
|
109
|
-
DateTime.parse @data["event"]["eventDate"] if @data.has_key?("event") && @data["event"].has_key?("eventDate")
|
110
|
-
end
|
111
|
-
|
112
|
-
def start_time
|
113
|
-
start_date
|
114
|
-
end
|
115
|
-
|
116
|
-
def end_time
|
117
|
-
nil
|
118
|
-
end
|
119
|
-
|
120
|
-
def end_date
|
121
|
-
nil
|
122
|
-
end
|
123
|
-
# The date and time that registration closes
|
124
|
-
def registration_close_date
|
125
|
-
DateTime.parse(@data["event"]["eventCloseDate"])
|
126
|
-
end
|
127
|
-
|
128
|
-
def category
|
129
|
-
primary_category
|
130
|
-
end
|
131
|
-
|
132
|
-
def user
|
133
|
-
email = contact_email
|
134
|
-
u = User.new
|
135
|
-
u.email = email if Validators.email(email)
|
136
|
-
# u.first_name = @data["meta"]["contactName"] || nil
|
137
|
-
# u.phone = @data["meta"]["contactPhone"] || nil
|
138
|
-
u
|
139
|
-
end
|
140
|
-
|
141
|
-
def desc_old
|
142
|
-
if @data.has_key?("event") && @data["event"].has_key?("briefDescription")
|
143
|
-
ret = @data["event"]["briefDescription"]
|
144
|
-
if @data["event"].has_key?("eventDetails") && @data["event"]["eventDetails"] != nil && @data["event"]["eventDetails"].has_key?("eventDetail")
|
145
|
-
eventDetail = @data["event"]["eventDetails"]["eventDetail"]
|
146
|
-
if eventDetail.class == Array
|
147
|
-
@data["event"]["eventDetails"]["eventDetail"].each do |detail|
|
148
|
-
ret +="<div><b>" + detail["eventDetailsName"] + ":</b> " + cleanup_reg_string(detail["eventDetailsValue"]) + "</div>"
|
149
|
-
end
|
150
|
-
else
|
151
|
-
#hash
|
152
|
-
ret +="<div><b>" + eventDetail["eventDetailsName"] + ":</b> " + cleanup_reg_string(eventDetail["eventDetailsValue"]) + "</div>"
|
153
|
-
end
|
154
|
-
end
|
155
|
-
return ret
|
156
|
-
elsif @data.has_key?("event") && @data["event"].has_key?("eventDescription")
|
157
|
-
return @data["event"]["eventDescription"]
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
def desc(length = :full)
|
162
|
-
if length == :full
|
163
|
-
@data["event"]["eventDescription"]
|
164
|
-
else
|
165
|
-
@data["event"]["briefDescription"]
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
# Content should be a array of hashes.
|
170
|
-
# [ {:title => "briefDescription", :type => "html", :content => "..." }]
|
171
|
-
#
|
172
|
-
# It should contain everything in briefDescription description and eventDetails.
|
173
|
-
# It should just be one big happy
|
174
|
-
#
|
175
|
-
# TODO: Need to order this by detail[:eventDetailsOrder]
|
176
|
-
# TODO: Add the other description blocks to this
|
177
|
-
def content
|
178
|
-
if @data["event"] and @data["event"]["eventDetails"]
|
179
|
-
|
180
|
-
if @data["event"]["eventDetails"]["eventDetail"].class == Array
|
181
|
-
return @data["event"]["eventDetails"]["eventDetail"].collect { |obj| {:title => obj[:eventDetailsName], :content => obj[:eventDetailsValue]} }
|
182
|
-
else
|
183
|
-
return [{:title => @data["event"]["eventDetails"]["eventDetail"]["eventDetailsName"],:content => @data["event"]["eventDetails"]["eventDetail"]["eventDetailsValue"]}]
|
184
|
-
end
|
185
|
-
|
186
|
-
else
|
187
|
-
return []
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
def cleanup_reg_string(input)
|
192
|
-
input.gsub("\r","").gsub("\n","").gsub("\"","""").gsub("\342\200\234","""").gsub("\342\200\235","""")
|
193
|
-
end
|
194
|
-
|
195
|
-
def contact_email
|
196
|
-
if @data.has_key?("event") && @data["event"].has_key?("eventContactEmail")
|
197
|
-
return @data["event"]["eventContactEmail"]
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
|
202
|
-
# EXAMPLE
|
203
|
-
# lazy load the data for some_crazy_method
|
204
|
-
# def some_crazy
|
205
|
-
# return @some_crazy unless @some_crazy.nil?
|
206
|
-
# @some_crazy = @data[:some_crazy_method_from_ats].split replace twist bla bla bla
|
207
|
-
# end
|
208
|
-
# local id
|
209
|
-
def self.find_by_id(id)
|
210
|
-
begin
|
211
|
-
doc = Nokogiri::XML(open("http://apij.active.com/regcenter/event/#{id}"))
|
212
|
-
puts "////////<br/>"
|
213
|
-
puts doc.to_s
|
214
|
-
puts "////////<br/>"
|
215
|
-
reg = RegCenter.new(Hash.from_xml(doc.to_s))
|
216
|
-
rescue Exception => e
|
217
|
-
raise RegCenterError, "Couldn't find Reg Center activity with the id of #{id} - #{e.inspect}"
|
218
|
-
return nil
|
219
|
-
end
|
220
|
-
reg
|
221
|
-
end
|
222
|
-
|
223
|
-
|
224
|
-
def self.get_asset_metadata(id)
|
225
|
-
c = Savon::Client.new("http://api.amp.active.com/asset-service/services/AssetService?wsdl")
|
226
|
-
c.request.headers["Api-Key"] = "6npky9t57235vps5cetm3s7k"
|
227
|
-
r = c.get_asset_metadata do |soap|
|
228
|
-
soap.namespace = "http://api.asset.services.active.com"
|
229
|
-
soap.body = "<context><userId></userId><applicationId></applicationId></context><assetId>#{id}</assetId>"
|
230
|
-
end
|
231
|
-
puts "==========="
|
232
|
-
puts r.to_hash[:get_asset_metadata_response][:out].inspect
|
233
|
-
return r
|
234
|
-
end
|
235
|
-
|
236
|
-
|
237
|
-
private
|
238
|
-
# def self.get_asset_by_id(id)
|
239
|
-
# puts "loading ATS"
|
240
|
-
# c = Savon::Client.new("http://api.amp.active.com/asset-service/services/AssetService")
|
241
|
-
# c.request.headers["Api-Key"] = "6npky9t57235vps5cetm3s7k"
|
242
|
-
# r = c.get_asset_by_id! do |soap|
|
243
|
-
# soap.namespace = "http://api.asset.services.active.com"
|
244
|
-
# soap.body = "<context><userId></userId><applicationId></applicationId></context><assetId>#{id}</assetId>"
|
245
|
-
# end
|
246
|
-
# return r
|
247
|
-
# end
|
248
|
-
#
|
249
|
-
# def load_metadata
|
250
|
-
# puts "loading ATS metadata"
|
251
|
-
# metadata = ATS.get_asset_metadata(@asset_id)
|
252
|
-
# @data.merge! Hash.from_xml(metadata.to_hash[:get_asset_metadata_response][:out])["importSource"]["asset"]
|
253
|
-
# @metadata_loaded=true
|
254
|
-
# end
|
255
|
-
|
256
|
-
# def get_app_api
|
257
|
-
# puts "loading reg center api"
|
258
|
-
# begin
|
259
|
-
# doc = Nokogiri::XML(open("http://apij.active.com/regcenter/event/#{@data[:id]}"))
|
260
|
-
# @data.merge! Hash.from_xml doc.to_s
|
261
|
-
# @api_data_loaded=true
|
262
|
-
# rescue
|
263
|
-
# raise RegCenterError, "Couldn't find Reg Center activity with the id of #{id}"
|
264
|
-
# return
|
265
|
-
# end
|
266
|
-
# end
|
267
|
-
|
268
|
-
end # end ats
|
269
|
-
end
|
270
|
-
end
|
data/lib/services/sanitize.rb
DELETED
@@ -1,108 +0,0 @@
|
|
1
|
-
# http://wonko.com/post/sanitize
|
2
|
-
|
3
|
-
# $Id: sanitize.rb 3 2005-04-05 12:51:14Z dwight $
|
4
|
-
#
|
5
|
-
# Copyright (c) 2005 Dwight Shih
|
6
|
-
# A derived work of the Perl version:
|
7
|
-
# Copyright (c) 2002 Brad Choate, bradchoate.com
|
8
|
-
#
|
9
|
-
# Permission is hereby granted, free of charge, to
|
10
|
-
# any person obtaining a copy of this software and
|
11
|
-
# associated documentation files (the "Software"), to
|
12
|
-
# deal in the Software without restriction, including
|
13
|
-
# without limitation the rights to use, copy, modify,
|
14
|
-
# merge, publish, distribute, sublicense, and/or sell
|
15
|
-
# copies of the Software, and to permit persons to
|
16
|
-
# whom the Software is furnished to do so, subject to
|
17
|
-
# the following conditions:
|
18
|
-
#
|
19
|
-
# The above copyright notice and this permission
|
20
|
-
# notice shall be included in all copies or
|
21
|
-
# substantial portions of the Software.
|
22
|
-
#
|
23
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY
|
24
|
-
# OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
25
|
-
# LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
26
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND
|
27
|
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
28
|
-
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
29
|
-
# OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
30
|
-
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
31
|
-
# OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
32
|
-
# OTHER DEALINGS IN THE SOFTWARE.
|
33
|
-
#
|
34
|
-
|
35
|
-
def sanitize( html, okTags='a href, b, br, i, p' )
|
36
|
-
# no closing tag necessary for these
|
37
|
-
soloTags = ["br","hr"]
|
38
|
-
|
39
|
-
# Build hash of allowed tags with allowed attributes
|
40
|
-
tags = okTags.downcase().split(',').collect!{ |s| s.split(' ') }
|
41
|
-
allowed = Hash.new
|
42
|
-
tags.each do |s|
|
43
|
-
key = s.shift
|
44
|
-
allowed[key] = s
|
45
|
-
end
|
46
|
-
|
47
|
-
# Analyze all <> elements
|
48
|
-
stack = Array.new
|
49
|
-
result = html.gsub( /(<.*?>)/m ) do | element |
|
50
|
-
if element =~ /\A<\/(\w+)/ then
|
51
|
-
# </tag>
|
52
|
-
tag = $1.downcase
|
53
|
-
if allowed.include?(tag) && stack.include?(tag) then
|
54
|
-
# If allowed and on the stack
|
55
|
-
# Then pop down the stack
|
56
|
-
top = stack.pop
|
57
|
-
out = "</#{top}>"
|
58
|
-
until top == tag do
|
59
|
-
top = stack.pop
|
60
|
-
out << "</#{top}>"
|
61
|
-
end
|
62
|
-
out
|
63
|
-
end
|
64
|
-
elsif element =~ /\A<(\w+)\s*\/>/
|
65
|
-
# <tag />
|
66
|
-
tag = $1.downcase
|
67
|
-
if allowed.include?(tag) then
|
68
|
-
"<#{tag} />"
|
69
|
-
end
|
70
|
-
elsif element =~ /\A<(\w+)/ then
|
71
|
-
# <tag ...>
|
72
|
-
tag = $1.downcase
|
73
|
-
if allowed.include?(tag) then
|
74
|
-
if ! soloTags.include?(tag) then
|
75
|
-
stack.push(tag)
|
76
|
-
end
|
77
|
-
if allowed[tag].length == 0 then
|
78
|
-
# no allowed attributes
|
79
|
-
"<#{tag}>"
|
80
|
-
else
|
81
|
-
# allowed attributes?
|
82
|
-
out = "<#{tag}"
|
83
|
-
while ( $' =~ /(\w+)=("[^"]+")/ )
|
84
|
-
attr = $1.downcase
|
85
|
-
valu = $2
|
86
|
-
if allowed[tag].include?(attr) then
|
87
|
-
out << " #{attr}=#{valu}"
|
88
|
-
end
|
89
|
-
end
|
90
|
-
out << ">"
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
# eat up unmatched leading >
|
97
|
-
while result.sub!(/\A([^<]*)>/m) { $1 } do end
|
98
|
-
|
99
|
-
# eat up unmatched trailing <
|
100
|
-
while result.sub!(/<([^>]*)\Z/m) { $1 } do end
|
101
|
-
|
102
|
-
# clean up the stack
|
103
|
-
if stack.length > 0 then
|
104
|
-
result << "</#{stack.reverse.join('></')}>"
|
105
|
-
end
|
106
|
-
|
107
|
-
result
|
108
|
-
end
|
data/lib/services/search.rb
DELETED
@@ -1,494 +0,0 @@
|
|
1
|
-
require 'net/http'
|
2
|
-
require 'json'
|
3
|
-
require 'cgi'
|
4
|
-
require 'mysql'
|
5
|
-
require 'active_record'
|
6
|
-
require 'digest/md5'
|
7
|
-
|
8
|
-
module Active
|
9
|
-
module Services
|
10
|
-
|
11
|
-
# we should remove this class and just replace with symbols
|
12
|
-
class Sort
|
13
|
-
def self.DATE_ASC
|
14
|
-
"date_asc"
|
15
|
-
end
|
16
|
-
def self.DATE_DESC
|
17
|
-
"date_desc"
|
18
|
-
end
|
19
|
-
def self.RELEVANCE
|
20
|
-
"relevance"
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
# we should remove this class and just replace with symbols
|
25
|
-
class Facet
|
26
|
-
def self.ACTIVITIES
|
27
|
-
"activities"
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
class Search
|
32
|
-
attr_accessor :api_key, :start_date, :end_date, :location, :channels, :split_media_type, :keywords, :search, :radius, :limit, :sort, :page, :offset, :latitude, :longitude,
|
33
|
-
:asset_type_id, :url,
|
34
|
-
:view, :facet, :sort, :num_results, :asset_ids, :dma, :city, :state, :country, :bounding_box
|
35
|
-
|
36
|
-
attr_reader :results, :endIndex, :pageSize, :searchTime, :numberOfResults, :end_point, :meta
|
37
|
-
|
38
|
-
SEARCH_URL = "http://search.active.com"
|
39
|
-
DEFAULT_TIMEOUT = 60
|
40
|
-
|
41
|
-
def initialize(data={})
|
42
|
-
self.asset_type_id = data[:asset_type_id]
|
43
|
-
self.url = data[:url]
|
44
|
-
self.api_key = data[:api_key] || ""
|
45
|
-
self.location = data[:location]
|
46
|
-
self.zips = data[:zips] || []
|
47
|
-
self.channels = data[:channels] || []
|
48
|
-
self.split_media_type = data[:split_media_type] || []
|
49
|
-
self.keywords = data[:keywords] || []
|
50
|
-
self.radius = data[:radius] || nil
|
51
|
-
self.limit = data[:limit] || "10"
|
52
|
-
self.sort = data[:sort] || Sort.DATE_ASC
|
53
|
-
self.page = data[:page] || "1"
|
54
|
-
self.offset = data[:offset] || "0"
|
55
|
-
self.view = data[:view] || "json"
|
56
|
-
self.facet = data[:facet] || Facet.ACTIVITIES
|
57
|
-
self.num_results = data[:num_results] || "10"
|
58
|
-
self.search = data[:search] || ""
|
59
|
-
self.start_date = data[:start_date] || "today"
|
60
|
-
self.end_date = data[:end_date] || "+"
|
61
|
-
self.asset_ids = data[:asset_ids] || []
|
62
|
-
self.asset_id = data[:asset_id] || ""
|
63
|
-
self.latitude = data[:latitude]
|
64
|
-
self.longitude = data[:longitude]
|
65
|
-
self.dma = data[:dma]
|
66
|
-
self.city = data[:city]
|
67
|
-
self.state = data[:state]
|
68
|
-
self.country = data[:country]
|
69
|
-
self.bounding_box = data[:bounding_box]
|
70
|
-
end
|
71
|
-
|
72
|
-
# Example
|
73
|
-
# Search.search( {:zips => "92121, 92078, 92114"} )
|
74
|
-
# or
|
75
|
-
# Search.new( {:zips => [92121, 92078, 92114]} )
|
76
|
-
def zips=(value)
|
77
|
-
if value.class == String
|
78
|
-
@zips = value.split(",").each { |k| k.strip! }
|
79
|
-
else
|
80
|
-
@zips = value
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def zips
|
85
|
-
@zips
|
86
|
-
end
|
87
|
-
|
88
|
-
def location=(value)
|
89
|
-
return if value.nil?
|
90
|
-
@location = CGI.escape(value)
|
91
|
-
end
|
92
|
-
|
93
|
-
def keywords=(value)
|
94
|
-
if value.class == String
|
95
|
-
@keywords = value.gsub(",", " ").split(" ").each { |k| k.strip! }
|
96
|
-
else
|
97
|
-
@keywords = value
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def channels=(value)
|
102
|
-
if value.class == String
|
103
|
-
@channels = value.split(",").each { |k| k.strip! }
|
104
|
-
else
|
105
|
-
@channels = value
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def asset_ids=(value)
|
110
|
-
@asset_ids = value
|
111
|
-
end
|
112
|
-
|
113
|
-
def bounding_box=(value)
|
114
|
-
return if value==nil
|
115
|
-
value = HashWithIndifferentAccess.new(value)
|
116
|
-
if value.has_key?("sw") && value.has_key?("ne")
|
117
|
-
@bounding_box=value
|
118
|
-
else
|
119
|
-
raise "bounding_box must be hash with keys sw and ne"
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
def asset_id=(value)
|
124
|
-
return if value.empty?
|
125
|
-
@asset_ids<<value
|
126
|
-
end
|
127
|
-
|
128
|
-
def end_point
|
129
|
-
meta_data = ""
|
130
|
-
loc_str = ""
|
131
|
-
# CHANNELS
|
132
|
-
channel_keys = []
|
133
|
-
@channels.each do |c|
|
134
|
-
c = c.to_sym
|
135
|
-
if Categories.CHANNELS.include?(c)
|
136
|
-
channel_keys << Categories.CHANNELS[c]
|
137
|
-
else
|
138
|
-
puts "/////////// Channel key not found [#{c}]"
|
139
|
-
end
|
140
|
-
end
|
141
|
-
meta_data += channel_keys.collect { |channel| "meta:channel=#{Search.double_encode_channel(channel)}" }.join("+OR+")
|
142
|
-
# SPLIT MEDIA TYPE
|
143
|
-
if split_media_type
|
144
|
-
meta_data += "+AND+" unless meta_data == ""
|
145
|
-
meta_data += split_media_type.collect { |type| "meta:splitMediaType=#{Search.double_encode_channel(type)}" }.join("+OR+")
|
146
|
-
end
|
147
|
-
# ASSET TYPE ID
|
148
|
-
if asset_type_id
|
149
|
-
meta_data += "+AND+" unless meta_data == ""
|
150
|
-
meta_data += "meta:assetTypeId=#{Search.double_encode_channel(asset_type_id)}"
|
151
|
-
end
|
152
|
-
# url
|
153
|
-
if url
|
154
|
-
meta_data += "+AND+" unless meta_data == ""
|
155
|
-
# url.gsub!(/\-/,"%252D")
|
156
|
-
url.gsub!(/\//,"%2F")
|
157
|
-
# URI.escape(url, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
|
158
|
-
meta_data += "site:#{url}"
|
159
|
-
# meta_data += "site:#{Search.double_encode_channel(url)}"
|
160
|
-
end
|
161
|
-
# ASSET IDS
|
162
|
-
unless asset_ids.empty?
|
163
|
-
meta_data += "+AND+" unless meta_data == ""
|
164
|
-
temp_ids = []
|
165
|
-
asset_ids.each do |id|
|
166
|
-
temp_ids << "meta:" + CGI.escape("assetId=#{id.gsub("-","%2d")}")
|
167
|
-
end
|
168
|
-
meta_data += temp_ids.join("+OR+")
|
169
|
-
end
|
170
|
-
# LOCATION
|
171
|
-
# 1 Look for :city, :state, :country
|
172
|
-
# 2 Look for zip codes
|
173
|
-
# 3 Look for lat lng
|
174
|
-
# 4 Look for a formatted string "San Diego, CA, US"
|
175
|
-
# 5 Look for a dma
|
176
|
-
if @city or @state or @country
|
177
|
-
if @city
|
178
|
-
meta_data += "+AND+" unless meta_data == ""
|
179
|
-
meta_data += "meta:city=#{Search.double_encode_channel(@city)}"
|
180
|
-
end
|
181
|
-
if @state
|
182
|
-
meta_data += "+AND+" unless meta_data == ""
|
183
|
-
meta_data += "meta:state=#{Search.double_encode_channel(@state)}"
|
184
|
-
end
|
185
|
-
elsif !@zips.empty?
|
186
|
-
loc_str = @zips.join(",")
|
187
|
-
elsif @latitude and @longitude
|
188
|
-
loc_str = "#{@latitude};#{@longitude}"
|
189
|
-
elsif @dma
|
190
|
-
meta_data += "+AND+" unless meta_data == ""
|
191
|
-
meta_data += "meta:dma=#{Search.double_encode_channel(@dma)}"
|
192
|
-
else
|
193
|
-
loc_str = @location
|
194
|
-
end
|
195
|
-
|
196
|
-
# BOUNDING BOX
|
197
|
-
unless @bounding_box.nil?
|
198
|
-
#The values in the GSA metadata are shifted to prevent negative values. This was done b/c lat/long
|
199
|
-
# are searched as a number range and the GSA doesn't allow negative values in number ranges.
|
200
|
-
# We shift latitude values by 90 and longitude values by 180.
|
201
|
-
if @bounding_box[:sw].class==String
|
202
|
-
#String :bounding_box => { :sw => "37.695141,-123.013657", :ne => "37.832371,-122.356979"}
|
203
|
-
latitude1 = @bounding_box[:sw].split(",").first.to_f+90
|
204
|
-
latitude2 = @bounding_box[:ne].split(",").first.to_f+90
|
205
|
-
longitude1 = @bounding_box[:sw].split(",").last.to_f+180
|
206
|
-
longitude2 = @bounding_box[:ne].split(",").last.to_f+180
|
207
|
-
else
|
208
|
-
#hash query[:bounding_box] = { :sw => [123, 10], :ne => [222,10] }
|
209
|
-
latitude1 = @bounding_box[:sw].first.to_f+90
|
210
|
-
latitude2 = @bounding_box[:ne].first.to_f+90
|
211
|
-
longitude1 = @bounding_box[:sw].last.to_f+180
|
212
|
-
longitude2 = @bounding_box[:ne].last.to_f+180
|
213
|
-
end
|
214
|
-
meta_data += "+AND+" unless meta_data == ""
|
215
|
-
meta_data += "meta:latitudeShifted:#{latitude1}..#{latitude2}+AND+meta:longitudeShifted:#{longitude1}..#{longitude2}"
|
216
|
-
end
|
217
|
-
|
218
|
-
# AND DATE
|
219
|
-
if @start_date.class == Date
|
220
|
-
@start_date = URI.escape(@start_date.strftime("%m/%d/%Y")).gsub(/\//,"%2F")
|
221
|
-
end
|
222
|
-
if @end_date.class == Date
|
223
|
-
@end_date = URI.escape(@end_date.strftime("%m/%d/%Y")).gsub(/\//,"%2F")
|
224
|
-
end
|
225
|
-
meta_data += "+AND+" unless meta_data == ""
|
226
|
-
meta_data += "meta:startDate:daterange:#{@start_date}..#{@end_date}"
|
227
|
-
|
228
|
-
# url = "#{SEARCH_URL}/search?api_key=#{@api_key}&num=#{@num_results}&page=#{@page}&l=#{loc_str}&f=#{@facet}&v=#{@view}&r=#{@radius}&s=#{@sort}&k=#{@keywords.join("+")}&m=#{meta_data}"
|
229
|
-
urla = ["#{SEARCH_URL}/search?api_key=#{@api_key}"]
|
230
|
-
urla << "num=#{@num_results}" if @num_results
|
231
|
-
urla << "page=#{@page}" if @page
|
232
|
-
urla << "l=#{loc_str}" unless loc_str.nil? or loc_str.empty?
|
233
|
-
urla << "f=#{@facet}" if @facet
|
234
|
-
urla << "v=#{@view}" if @view
|
235
|
-
urla << "r=#{@radius}" if @radius
|
236
|
-
urla << "s=#{@sort}" if @sort
|
237
|
-
urla << "k=#{@keywords.join("+")}" if @keywords and !@keywords.empty?
|
238
|
-
urla << "m=#{meta_data}" if meta_data
|
239
|
-
|
240
|
-
return urla.join("&")
|
241
|
-
end
|
242
|
-
|
243
|
-
# TODO the seach method should not raise an error.
|
244
|
-
def search
|
245
|
-
searchurl = URI.parse(end_point)
|
246
|
-
req = Net::HTTP::Get.new(searchurl.path)
|
247
|
-
http = Net::HTTP.new(searchurl.host, searchurl.port)
|
248
|
-
http.read_timeout = DEFAULT_TIMEOUT
|
249
|
-
|
250
|
-
puts "Active Search [GET] #{"#{searchurl.path}?#{searchurl.query}"}"
|
251
|
-
res = http.start { |http|
|
252
|
-
http.get("#{searchurl.path}?#{searchurl.query}")
|
253
|
-
}
|
254
|
-
|
255
|
-
if (200..307).include?(res.code.to_i)
|
256
|
-
begin
|
257
|
-
parsed_json = JSON.parse(res.body)
|
258
|
-
@endIndex = parsed_json["endIndex"]
|
259
|
-
@pageSize = parsed_json["pageSize"]
|
260
|
-
@searchTime = parsed_json["searchTime"]
|
261
|
-
# GSA will only return 1000 events but will give us a number larger then 1000.
|
262
|
-
@numberOfResults = (parsed_json["numberOfResults"] <= 1000) ? parsed_json["numberOfResults"] : 1000
|
263
|
-
@results = parsed_json['_results'].collect { |a| Activity.new(GSA.new(a)) }
|
264
|
-
|
265
|
-
begin
|
266
|
-
Active.CACHE.set( Digest::MD5.hexdigest(end_point), self) if Active.CACHE
|
267
|
-
rescue Exception => e
|
268
|
-
end
|
269
|
-
|
270
|
-
rescue JSON::ParserError => e
|
271
|
-
raise RuntimeError, "JSON::ParserError json=#{res.body}"
|
272
|
-
@endIndex = 0
|
273
|
-
@pageSize = 0
|
274
|
-
@searchTime = 0
|
275
|
-
@numberOfResults = 0
|
276
|
-
@results = []
|
277
|
-
end
|
278
|
-
|
279
|
-
else
|
280
|
-
raise RuntimeError, "Active Search responded with a #{res.code} for your query."
|
281
|
-
end
|
282
|
-
end
|
283
|
-
|
284
|
-
# examples
|
285
|
-
#
|
286
|
-
#
|
287
|
-
# = Keywords =
|
288
|
-
# Keywords can be set like this
|
289
|
-
# Search.new({:keywords => "Dog,Cat,Cow"})
|
290
|
-
# Search.new({:keywords => %w(Dog Cat Cow)})
|
291
|
-
# Search.new({:keywords => ["Dog","Cat","Cow"]})
|
292
|
-
#
|
293
|
-
# = Channels and MediaType =
|
294
|
-
# Search.new({:channels => [:running], :split_media_type => ["5k"]})
|
295
|
-
# for a list of channels and splitMediaTypes please see http://developer.active.com/docs/Activecom_Search_API_Reference
|
296
|
-
#
|
297
|
-
# = Location =
|
298
|
-
# The location will be set in this order and will override other values. For example is you set a zip code and a location only the zip will be used.
|
299
|
-
#
|
300
|
-
# 1 Look for zip codes
|
301
|
-
# Search.search( {:zips => "92121, 92078, 92114"} )
|
302
|
-
# Search.search( {:zips => %w(92121, 92078, 92114)} )
|
303
|
-
# Search.search( {:zips => [92121, 92078, 92114]} )
|
304
|
-
#
|
305
|
-
# 2 Look for lat lng
|
306
|
-
# Search.search( {:latitude=>"37.785895", :longitude=>"-122.40638"} )
|
307
|
-
#
|
308
|
-
# 3 Look for a formatted string "San Diego, CA, US"
|
309
|
-
# Search.search( {:location = "San Diego, CA, US"} )
|
310
|
-
#
|
311
|
-
# 4 Look for a DMA
|
312
|
-
# Search.new({:dma=>"San Francisco - Oakland - San Jose"})
|
313
|
-
#
|
314
|
-
# = How to look at the results =
|
315
|
-
#
|
316
|
-
#
|
317
|
-
# http://developer.active.com/docs/Activecom_Search_API_Reference
|
318
|
-
# returns an array of results and query info
|
319
|
-
def self.search(data=nil)
|
320
|
-
search = Search.new(data)
|
321
|
-
search_hash = Digest::MD5.hexdigest(search.end_point)
|
322
|
-
puts "search_hash #{search_hash}"
|
323
|
-
cache = Search.return_cached(search_hash)
|
324
|
-
if cache != nil
|
325
|
-
return cache
|
326
|
-
else
|
327
|
-
search.search
|
328
|
-
return search
|
329
|
-
end
|
330
|
-
end
|
331
|
-
|
332
|
-
def self.return_cached key
|
333
|
-
if Active.CACHE
|
334
|
-
begin
|
335
|
-
cached_version = Active.CACHE.get(key)
|
336
|
-
rescue Exception => e
|
337
|
-
return nil
|
338
|
-
end
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
if cached_version
|
343
|
-
puts "Active Search [CACHE] #{key}"
|
344
|
-
return cached_version
|
345
|
-
end
|
346
|
-
end
|
347
|
-
nil
|
348
|
-
end
|
349
|
-
|
350
|
-
|
351
|
-
def []
|
352
|
-
@results
|
353
|
-
end
|
354
|
-
|
355
|
-
private
|
356
|
-
def self.double_encode_channel str
|
357
|
-
str = URI.escape(str, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
|
358
|
-
str = URI.escape(str, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
|
359
|
-
str.gsub!(/\-/,"%252D")
|
360
|
-
str
|
361
|
-
end
|
362
|
-
end
|
363
|
-
|
364
|
-
# TODO move to a reflection service
|
365
|
-
class Categories
|
366
|
-
def self.CHANNELS
|
367
|
-
{
|
368
|
-
:active_lifestyle => '',
|
369
|
-
:fitness => 'Fitness',
|
370
|
-
:body_building => 'Fitness\Body Building',
|
371
|
-
:boxing => 'Fitness\Boxing',
|
372
|
-
:weight_lifting => 'Fitness\Weight Lifting',
|
373
|
-
:gear => 'Gear',
|
374
|
-
:lifestyle_vehicles => 'Lifestyle Vehicles',
|
375
|
-
:mind_mody => 'Mind & Body',
|
376
|
-
:meditation => 'Mind & Body\Meditation',
|
377
|
-
:pilates => 'Mind & Body\Pilates',
|
378
|
-
:yoga => 'Mind & Body\Yoga',
|
379
|
-
:nutrition => 'Nutrition',
|
380
|
-
:travel => 'Travel',
|
381
|
-
:women => 'Women',
|
382
|
-
:other => 'Other',
|
383
|
-
:corporate => 'Corporate',
|
384
|
-
:not_specified => 'Not Specified',
|
385
|
-
:unknown => 'Unknown',
|
386
|
-
:special_interest => 'Special+Interest',
|
387
|
-
:giving => 'Giving',
|
388
|
-
:parks_recreation => 'Parks & Recreation',
|
389
|
-
:gear => 'Parks & Recreation\Gear',
|
390
|
-
:mind_body => 'Parks & Recreation\Mind & Body',
|
391
|
-
:travel => 'Parks & Recreation\Travel',
|
392
|
-
:vehicles => 'Parks & Recreation\Vehicles',
|
393
|
-
:women => 'Parks & Recreation\Women',
|
394
|
-
:reunions => 'Reunions',
|
395
|
-
:sports => 'Sports',
|
396
|
-
:action_sports => 'Action Sports',
|
397
|
-
:auto_racing => 'Action Sports\Auto Racing',
|
398
|
-
:bmx => 'Action Sports\BMX',
|
399
|
-
:dirt_bike_racing => 'Action Sports\Dirt Bike Racing',
|
400
|
-
:motocross => 'Action Sports\Motocross',
|
401
|
-
:motorcycle_racing => 'Action Sports\Motorcycle Racing',
|
402
|
-
:skateboarding => 'Action Sports\Skateboarding',
|
403
|
-
:skydiving => 'Action Sports\Skydiving',
|
404
|
-
:surfing => 'Action Sports\Surfing',
|
405
|
-
:wake_kite_boarding => 'Action Sports\Wake/Kite Boarding',
|
406
|
-
:water_skiing => 'Action Sports\Water Skiing',
|
407
|
-
:wind_surfing => 'Action Sports\Wind Surfing',
|
408
|
-
:baseball => 'Baseball',
|
409
|
-
:little_league_baseball => 'Baseball\Little League Baseball',
|
410
|
-
:tee_ball => 'Baseball\Tee Ball',
|
411
|
-
:basketball => 'Basketball',
|
412
|
-
:cheerleading => 'Cheerleading',
|
413
|
-
:cycling => 'Cycling',
|
414
|
-
:field_hockey => 'Field Hockey',
|
415
|
-
:football => 'Football',
|
416
|
-
:flag_football => 'Football\Flag Football',
|
417
|
-
:football_au => 'Football\Football-AU',
|
418
|
-
:golf => 'Golf',
|
419
|
-
:ice_hockey => 'Ice Hockey',
|
420
|
-
:lacrosse => 'Lacrosse',
|
421
|
-
:more_sports => 'More Sports',
|
422
|
-
:adventure_racing => 'More Sports\Adventure Racing',
|
423
|
-
:archery => 'More Sports\Archery',
|
424
|
-
:badminton => 'More Sports\Badminton',
|
425
|
-
:billiards => 'More Sports\Billiards',
|
426
|
-
:bowling => 'More Sports\Bowling',
|
427
|
-
:cricket => 'More Sports\Cricket',
|
428
|
-
:croquet => 'More Sports\Croquet',
|
429
|
-
:curling => 'More Sports\Curling',
|
430
|
-
:dance => 'More Sports\Dance',
|
431
|
-
:disc_sports => 'More Sports\Disc Sports',
|
432
|
-
:dodgeball => 'More Sports\Dodgeball',
|
433
|
-
:duathlon => 'More Sports\Duathlon',
|
434
|
-
:equestrian => 'More Sports\Equestrian',
|
435
|
-
:fencing => 'More Sports\Fencing',
|
436
|
-
:figure_skating => 'More Sports\Figure Skating',
|
437
|
-
:gymnastics => 'More Sports\Gymnastics',
|
438
|
-
:inline_hockey => 'More Sports\Inline Hockey',
|
439
|
-
:inline_skating => 'More Sports\Inline Skating',
|
440
|
-
:kickball => 'More Sports\Kickball',
|
441
|
-
:martial_arts => 'More Sports\Martial Arts',
|
442
|
-
:paintball => 'More Sports\Paintball',
|
443
|
-
:polo => 'More Sports\Polo',
|
444
|
-
:racquetball => 'More Sports\Racquetball',
|
445
|
-
:rowing => 'More Sports\Rowing',
|
446
|
-
:rugby => 'More Sports\Rugby',
|
447
|
-
:scouting => 'More Sports\Scouting',
|
448
|
-
:scuba_diving => 'More Sports\Scuba Diving',
|
449
|
-
:skating => 'More Sports\Skating',
|
450
|
-
:squash => 'More Sports\Squash',
|
451
|
-
:ultimate_frisbee => 'More Sports\Ultimate Frisbee',
|
452
|
-
:water_polo => 'More Sports\Water Polo',
|
453
|
-
:mountain_biking => 'Mountain Biking',
|
454
|
-
:outdoors => 'Outdoors',
|
455
|
-
:canoeing => 'Outdoors\Canoeing',
|
456
|
-
:climbing => 'Outdoors\Climbing',
|
457
|
-
:hiking => 'Outdoors\Hiking',
|
458
|
-
:kayaking => 'Outdoors\Kayaking',
|
459
|
-
:orienteering => 'Outdoors\Orienteering',
|
460
|
-
:outrigging => 'Outdoors\Outrigging',
|
461
|
-
:rafting => 'Outdoors\Rafting',
|
462
|
-
:racquetball => 'Racquetball',
|
463
|
-
:rugby => 'Rugby',
|
464
|
-
:running => 'Running',
|
465
|
-
:cross_country => 'Running\Cross Country',
|
466
|
-
:marathon_running => 'Running\Marathon Running',
|
467
|
-
:track_field => 'Running\Track & Field',
|
468
|
-
:trail_running => 'Running\Trail Running',
|
469
|
-
:sailing => 'Sailing',
|
470
|
-
:snow_sports => 'Snow Sports',
|
471
|
-
:skiing => 'Snow Sports\Skiing',
|
472
|
-
:snowboarding => 'Snow Sports\Snowboarding',
|
473
|
-
:snowshoeing => 'Snow Sports\Snowshoeing',
|
474
|
-
:soccer => 'Soccer',
|
475
|
-
:softball => 'Softball',
|
476
|
-
:softball_dixie => 'Softball\Softball-Dixie',
|
477
|
-
:softball_fast_pitch => 'Softball\Softball-Fast Pitch',
|
478
|
-
:softball_slow_pitch => 'Softball\Softball-Slow Pitch',
|
479
|
-
:squash => 'Squash',
|
480
|
-
:swimming => 'Swimming',
|
481
|
-
:diving => 'Swimming\Diving',
|
482
|
-
:tennis => 'Tennis',
|
483
|
-
:other_tennis => 'Tennis\Other Tennis',
|
484
|
-
:usta => 'Tennis\USTA',
|
485
|
-
:triathlon => 'Triathlon',
|
486
|
-
:volleyball => 'Volleyball',
|
487
|
-
:walking => 'Walking',
|
488
|
-
:wrestling => 'Wrestling'
|
489
|
-
}
|
490
|
-
end
|
491
|
-
end
|
492
|
-
|
493
|
-
end
|
494
|
-
end
|