muck-services 0.1.23 → 0.1.24
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +4 -2
- data/Rakefile +3 -3
- data/VERSION +1 -1
- data/app/models/entry.rb +88 -91
- data/app/models/oai_endpoint.rb +1 -13
- data/app/models/personal_recommendation.rb +6 -2
- data/app/models/service.rb +14 -14
- data/app/views/recommendations/index.pjs.erb +4 -3
- data/config/muck_services_routes.rb +1 -0
- data/lib/active_record/acts/muck_recommendations.rb +2 -0
- data/muck-services.gemspec +4 -6
- data/public/stylesheets/muck-services.css +1 -1
- data/test/rails_root/config/environment.rb +1 -0
- data/test/rails_root/test/unit/aggregation_test.rb +4 -38
- data/test/rails_root/test/unit/feed_test.rb +32 -5
- data/test/rails_root/test/unit/oai_endpoint_test.rb +37 -10
- data/test/rails_root/test/unit/service_category_test.rb +1 -1
- data/test/rails_root/test/unit/service_test.rb +57 -4
- metadata +2 -3
- data/app/views/services/_personal_recommendations.html.erb +0 -5
data/README.rdoc
CHANGED
@@ -78,8 +78,10 @@ muck-services uses muck-comments and muck-activities to add comments to entries
|
|
78
78
|
muck-services also uses muck-shares and muck-activities to share entries with other users. Again you can omit these gems if you don't desire the share capability.
|
79
79
|
|
80
80
|
To turn on this functionality add these lines to your global_config.yml file:
|
81
|
-
|
82
|
-
|
81
|
+
inform_admin_of_global_feed: true # If true then the 'admin_email' will recieve an email anytime a global feed (one that is not
|
82
|
+
# attached to any object) is added.
|
83
|
+
enable_services_comments: true # Enables or disables comments in the frame that wraps content as a user browses recommendation results
|
84
|
+
enable_services_shares: true # Enables or disables sharing in the frame that wraps content as a user browses recommendation results
|
83
85
|
|
84
86
|
To install these gems:
|
85
87
|
sudo gem install muck-comments
|
data/Rakefile
CHANGED
@@ -3,6 +3,9 @@ require 'rake'
|
|
3
3
|
require 'rake/testtask'
|
4
4
|
require 'rake/rdoctask'
|
5
5
|
|
6
|
+
desc 'Default: run unit tests.'
|
7
|
+
task :default => :test
|
8
|
+
|
6
9
|
desc 'Translate this gem'
|
7
10
|
task :translate do
|
8
11
|
file = File.join(File.dirname(__FILE__), 'locales', 'en.yml')
|
@@ -35,9 +38,6 @@ Rake::TestTask.new(:test) do |t|
|
|
35
38
|
t.verbose = true
|
36
39
|
end
|
37
40
|
|
38
|
-
task :test => :check_dependencies
|
39
|
-
task :default => :test
|
40
|
-
|
41
41
|
begin
|
42
42
|
require 'jeweler'
|
43
43
|
Jeweler::Tasks.new do |gem|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.24
|
data/app/models/entry.rb
CHANGED
@@ -46,10 +46,6 @@ class Entry < ActiveRecord::Base
|
|
46
46
|
self.direct_link.nil? ? self.permalink : self.direct_link
|
47
47
|
end
|
48
48
|
|
49
|
-
def self.top_tags(tags = nil)
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
49
|
def self.search(search_terms, grain_size = nil, language = "en", limit = 10, offset = 0, operator = :or)
|
54
50
|
raise MuckServices::Exceptions::LanguageNotSupported, I18n.t('muck.services.language_not_supported') unless Recommender::Languages.supported_languages.include?(language)
|
55
51
|
query = ((!grain_size.nil? && grain_size != 'all') ? (search_terms + ") AND (grain_size:#{grain_size}") : search_terms) + ") AND (aggregation:#{Aggregation.global_feeds_id}"
|
@@ -63,8 +59,8 @@ class Entry < ActiveRecord::Base
|
|
63
59
|
Entry.find(:first, :conditions => ['permalink = ? OR direct_link = ?', uri, uri], :order => 'direct_link IS NULL DESC') || Entry.new(:permalink => uri)
|
64
60
|
end
|
65
61
|
|
66
|
-
def
|
67
|
-
sql = "SELECT recommendations.id,
|
62
|
+
def recommendation_entries(limit = 20, order = "relevance", details = false, omit_feeds = nil)
|
63
|
+
sql = "SELECT recommendations.dest_entry_id AS id, entries.permalink, entries.title, entries.description, entries.direct_link, feeds.short_title AS collection "
|
68
64
|
sql << ", relevance_calculated_at, relevance, clicks, avg_time_at_dest AS avg_time_on_target, author, published_at " if details == true
|
69
65
|
sql << "FROM recommendations "
|
70
66
|
sql << "INNER JOIN entries ON recommendations.dest_entry_id = entries.id "
|
@@ -76,8 +72,8 @@ class Entry < ActiveRecord::Base
|
|
76
72
|
Entry.find_by_sql([sql,self.id])
|
77
73
|
end
|
78
74
|
|
79
|
-
def
|
80
|
-
sql = "SELECT recommendations.
|
75
|
+
def recommendations(limit = 20, order = "relevance", details = false, omit_feeds = nil)
|
76
|
+
sql = "SELECT recommendations.id, dest_entry_id, entries.permalink, entries.title, entries.description, entries.direct_link, feeds.short_title AS collection "
|
81
77
|
sql << ", relevance_calculated_at, relevance, clicks, avg_time_at_dest AS avg_time_on_target, author, published_at " if details == true
|
82
78
|
sql << "FROM recommendations "
|
83
79
|
sql << "INNER JOIN entries ON recommendations.dest_entry_id = entries.id "
|
@@ -89,90 +85,13 @@ class Entry < ActiveRecord::Base
|
|
89
85
|
Entry.find_by_sql([sql,self.id])
|
90
86
|
end
|
91
87
|
|
92
|
-
def self.track_time_on_page(session, uri)
|
93
|
-
recommendation_id = session[:last_clicked_recommendation]
|
94
|
-
if !recommendation_id.nil?
|
95
|
-
time_on_page = (Time.now - session[:last_clicked_recommendation_time].to_f).to_i
|
96
|
-
|
97
|
-
# if they spend longer than two minutes on a page, we don't infer anything
|
98
|
-
if time_on_page > 5 and time_on_page < 120
|
99
|
-
if normalized_uri(uri) != session[:last_clicked_recommendation_uri]
|
100
|
-
recommendation = Recommendation.find(recommendation_id)
|
101
|
-
entry = Entry.find(recommendation.entry_id)
|
102
|
-
new_avg = (recommendation.avg_time_at_dest*recommendation.clicks - @@default_time_on_page + time_on_page)/recommendation.clicks
|
103
|
-
recommendation.avg_time_at_dest = new_avg
|
104
|
-
recommendation.save!
|
105
|
-
entry.rank_recommendations
|
106
|
-
session[:last_clicked_recommendation] = nil
|
107
|
-
end
|
108
|
-
else
|
109
|
-
session[:last_clicked_recommendation] = nil if time_on_page > 5
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
def self.avg_time(clicks, old_avg, time_on_page)
|
115
|
-
return (old_avg*(clicks-1) + time_on_page)/clicks
|
116
|
-
end
|
117
|
-
|
118
|
-
def self.track_click(session, recommendation_id, referrer, redirect_type = "direct_link", requester = "unknown", user_agent = "unknown")
|
119
|
-
# look up the recommendation
|
120
|
-
recommendation = Recommendation.find(recommendation_id)
|
121
|
-
return "" if !recommendation
|
122
|
-
|
123
|
-
# get the entries being linked from and to
|
124
|
-
entry = Entry.find(recommendation.entry_id)
|
125
|
-
target = Entry.find(recommendation.dest_entry_id)
|
126
|
-
|
127
|
-
# get the list of recommendations that have been clicked during this session
|
128
|
-
clicks = session[:rids] || Array.new
|
129
|
-
|
130
|
-
# redirect to our frame page
|
131
|
-
redirect = "/visits/#{recommendation.dest_entry_id}"
|
132
|
-
|
133
|
-
# track the time on the last page
|
134
|
-
track_time_on_page(session, redirect)
|
135
|
-
|
136
|
-
# if this is first time the user clicked on this recommendation during this session
|
137
|
-
if !clicks.include?(recommendation_id)
|
138
|
-
|
139
|
-
# add this recommendation to the end of the list
|
140
|
-
clicks << recommendation_id
|
141
|
-
session[:rids] = clicks
|
142
|
-
|
143
|
-
# update the click time
|
144
|
-
recommendation.avg_time_at_dest = ((recommendation.avg_time_at_dest*recommendation.clicks) + @@default_time_on_page)/(recommendation.clicks + 1)
|
145
|
-
recommendation.clicks += 1
|
146
|
-
recommendation.save!
|
147
|
-
|
148
|
-
# store info about this click in the session
|
149
|
-
now = Time.now
|
150
|
-
session[:last_clicked_recommendation] = recommendation_id
|
151
|
-
session[:last_clicked_recommendation_time] = now
|
152
|
-
session[:last_clicked_recommendation_uri] = redirect
|
153
|
-
|
154
|
-
# update the recommendation cache for the entry
|
155
|
-
entry.rank_recommendations if entry
|
156
|
-
|
157
|
-
# track the click in the db
|
158
|
-
Click.create(:recommendation_id => recommendation_id, :when => now, :referrer => referrer, :requester => requester, :user_agent => user_agent)
|
159
|
-
end
|
160
|
-
|
161
|
-
return redirect
|
162
|
-
end
|
163
|
-
|
164
88
|
def relevant_recommendations(limit = 5, order = "relevance", details = false, omit_feeds = nil)
|
165
89
|
return self.recommendations(limit, order, details, omit_feeds)
|
166
90
|
end
|
167
91
|
|
168
|
-
def self.truncate_words(text, length = 30, end_string = ' ...')
|
169
|
-
words = text.split()
|
170
|
-
words[0..(length-1)].join(' ') + (words.length > length ? end_string : '')
|
171
|
-
end
|
172
|
-
|
173
92
|
def ranked_recommendations(limit = 5, order = "mixed", details = false, omit_feeds = nil)
|
174
|
-
return
|
175
|
-
return
|
93
|
+
return self.recommendations(limit, "clicks DESC, relevance", details, omit_feeds) if order == "clicks"
|
94
|
+
return self.recommendations(limit, "relevance", details, omit_feeds) if (order == "relevance" || details == true)
|
176
95
|
return relevant_recommendations_filtered(limit, details, omit_feeds) if omit_feeds != nil
|
177
96
|
|
178
97
|
recs = []
|
@@ -190,6 +109,11 @@ class Entry < ActiveRecord::Base
|
|
190
109
|
return recs
|
191
110
|
end
|
192
111
|
|
112
|
+
def json_recommendations(limit = 5, order = "mixed", details = false, omit_feeds = nil)
|
113
|
+
recs = ranked_recommendations(limit, order, details, omit_feeds)
|
114
|
+
(recs.nil? || recs.empty?) ? "" : ActiveSupport::JSON.encode(recs)
|
115
|
+
end
|
116
|
+
|
193
117
|
def relevant_recommendations_filtered(limit, details, omit_feeds)
|
194
118
|
# get recommendations for the entry from the recommendations table
|
195
119
|
recs = self.recommendations(limit, "mixed", details, omit_feeds)
|
@@ -222,10 +146,6 @@ class Entry < ActiveRecord::Base
|
|
222
146
|
return (popular_recs + relevant_recs + other_recs)[0..limit]
|
223
147
|
end
|
224
148
|
|
225
|
-
def json_recommendations(limit = 5, order = "mixed", details = false, omit_feeds = nil)
|
226
|
-
ActiveSupport::JSON.encode(ranked_recommendations(limit, order, details, omit_feeds))
|
227
|
-
end
|
228
|
-
|
229
149
|
def rank_recommendations
|
230
150
|
return
|
231
151
|
# get recommendations for the entry from the recommendations table
|
@@ -304,6 +224,83 @@ class Entry < ActiveRecord::Base
|
|
304
224
|
return threshold > 5 ? threshold : 5
|
305
225
|
end
|
306
226
|
|
227
|
+
def self.avg_time(clicks, old_avg, time_on_page)
|
228
|
+
return (old_avg*(clicks-1) + time_on_page)/clicks
|
229
|
+
end
|
230
|
+
|
231
|
+
def self.track_time_on_page(session, uri)
|
232
|
+
recommendation_id = session[:last_clicked_recommendation]
|
233
|
+
if !recommendation_id.nil?
|
234
|
+
time_on_page = (Time.now - session[:last_clicked_recommendation_time].to_f).to_i
|
235
|
+
|
236
|
+
# if they spend longer than two minutes on a page, we don't infer anything
|
237
|
+
if time_on_page > 5 and time_on_page < 120
|
238
|
+
if normalized_uri(uri) != session[:last_clicked_recommendation_uri]
|
239
|
+
recommendation = Recommendation.find(recommendation_id)
|
240
|
+
entry = Entry.find(recommendation.entry_id)
|
241
|
+
new_avg = (recommendation.avg_time_at_dest*recommendation.clicks - @@default_time_on_page + time_on_page)/recommendation.clicks
|
242
|
+
recommendation.avg_time_at_dest = new_avg
|
243
|
+
recommendation.save!
|
244
|
+
entry.rank_recommendations
|
245
|
+
session[:last_clicked_recommendation] = nil
|
246
|
+
end
|
247
|
+
else
|
248
|
+
session[:last_clicked_recommendation] = nil if time_on_page > 5
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def self.track_click(session, recommendation_id, referrer, redirect_type = "direct_link", requester = "unknown", user_agent = "unknown")
|
254
|
+
# look up the recommendation
|
255
|
+
recommendation = Recommendation.find(recommendation_id)
|
256
|
+
return "" if !recommendation
|
257
|
+
|
258
|
+
# get the entries being linked from and to
|
259
|
+
entry = Entry.find(recommendation.entry_id)
|
260
|
+
target = Entry.find(recommendation.dest_entry_id)
|
261
|
+
|
262
|
+
# get the list of recommendations that have been clicked during this session
|
263
|
+
clicks = session[:rids] || Array.new
|
264
|
+
|
265
|
+
# redirect to our frame page
|
266
|
+
redirect = "/visits/#{recommendation.dest_entry_id}"
|
267
|
+
|
268
|
+
# track the time on the last page
|
269
|
+
track_time_on_page(session, redirect)
|
270
|
+
|
271
|
+
# if this is first time the user clicked on this recommendation during this session
|
272
|
+
if !clicks.include?(recommendation_id)
|
273
|
+
|
274
|
+
# add this recommendation to the end of the list
|
275
|
+
clicks << recommendation_id
|
276
|
+
session[:rids] = clicks
|
277
|
+
|
278
|
+
# update the click time
|
279
|
+
recommendation.avg_time_at_dest = ((recommendation.avg_time_at_dest*recommendation.clicks) + @@default_time_on_page)/(recommendation.clicks + 1)
|
280
|
+
recommendation.clicks += 1
|
281
|
+
recommendation.save!
|
282
|
+
|
283
|
+
# store info about this click in the session
|
284
|
+
now = Time.now
|
285
|
+
session[:last_clicked_recommendation] = recommendation_id
|
286
|
+
session[:last_clicked_recommendation_time] = now
|
287
|
+
session[:last_clicked_recommendation_uri] = redirect
|
288
|
+
|
289
|
+
# update the recommendation cache for the entry
|
290
|
+
entry.rank_recommendations if entry
|
291
|
+
|
292
|
+
# track the click in the db
|
293
|
+
Click.create(:recommendation_id => recommendation_id, :when => now, :referrer => referrer, :requester => requester, :user_agent => user_agent)
|
294
|
+
end
|
295
|
+
|
296
|
+
return redirect
|
297
|
+
end
|
298
|
+
|
299
|
+
def self.truncate_words(text, length = 30, end_string = ' ...')
|
300
|
+
words = text.split()
|
301
|
+
words[0..(length-1)].join(' ') + (words.length > length ? end_string : '')
|
302
|
+
end
|
303
|
+
|
307
304
|
protected
|
308
305
|
|
309
306
|
# def self.redirect_uri(target, referrer, redirect_type)
|
data/app/models/oai_endpoint.rb
CHANGED
@@ -14,18 +14,6 @@
|
|
14
14
|
# created_at :datetime
|
15
15
|
# updated_at :datetime
|
16
16
|
#
|
17
|
-
|
18
|
-
# == Schema Information
|
19
|
-
#
|
20
|
-
# Table name: oai_endpoints
|
21
|
-
#
|
22
|
-
# id :integer(4) not null, primary key
|
23
|
-
# uri :string(2083)
|
24
|
-
# display_uri :string(2083)
|
25
|
-
# metadata_prefix :string(255)
|
26
|
-
# title :string(1000)
|
27
|
-
# short_title :string(100)
|
28
|
-
#
|
29
17
|
class OaiEndpoint < ActiveRecord::Base
|
30
18
|
|
31
19
|
belongs_to :contributor, :class_name => 'User', :foreign_key => 'contributor_id'
|
@@ -33,7 +21,7 @@ class OaiEndpoint < ActiveRecord::Base
|
|
33
21
|
|
34
22
|
validates_presence_of :uri
|
35
23
|
|
36
|
-
named_scope :banned, :conditions => "status
|
24
|
+
named_scope :banned, :conditions => ["status = ?", MuckServices::Status::BANNED]
|
37
25
|
named_scope :valid, :conditions => "status >= 0"
|
38
26
|
named_scope :by_title, :order => "title ASC"
|
39
27
|
named_scope :recent, lambda { { :conditions => ['created_at > ?', 1.week.ago] } }
|
@@ -13,10 +13,14 @@
|
|
13
13
|
#
|
14
14
|
|
15
15
|
class PersonalRecommendation < ActiveRecord::Base
|
16
|
-
|
16
|
+
unloadable
|
17
|
+
|
17
18
|
belongs_to :personal_recommendable, :polymorphic => true
|
18
19
|
belongs_to :destination, :polymorphic => true
|
19
|
-
|
20
|
+
|
21
|
+
belongs_to :entry, :class_name => 'Entry', :foreign_key => 'destination_id', :include => :feed
|
22
|
+
belongs_to :user, :class_name => 'User', :foreign_key => 'destination_id'
|
23
|
+
|
20
24
|
named_scope :limit, lambda { |num| { :limit => num } }
|
21
25
|
named_scope :recent, lambda { { :conditions => ['created_at > ?', 1.week.ago] } }
|
22
26
|
named_scope :newest, :order => "created_at DESC"
|
data/app/models/service.rb
CHANGED
@@ -230,7 +230,7 @@ class Service < ActiveRecord::Base
|
|
230
230
|
# will result in the values being repopulated from the database
|
231
231
|
def self.get_photo_services(refresh_services = false)
|
232
232
|
@photo_services = nil if refresh_services
|
233
|
-
@photo_services ||= get_services(refresh_services).find_all{|service| service.service_category.name == 'Photos' }
|
233
|
+
@photo_services ||= get_services(refresh_services).find_all{|service| !service.service_category.blank? && service.service_category.name == 'Photos' }
|
234
234
|
end
|
235
235
|
|
236
236
|
# Get all video services
|
@@ -239,7 +239,7 @@ class Service < ActiveRecord::Base
|
|
239
239
|
# will result in the values being repopulated from the database
|
240
240
|
def self.get_video_services(refresh_services = false)
|
241
241
|
@video_services = nil if refresh_services
|
242
|
-
@video_services ||= get_services(refresh_services).find_all{|service| service.service_category.name == 'Videos' }
|
242
|
+
@video_services ||= get_services(refresh_services).find_all{|service| !service.service_category.blank? && service.service_category.name == 'Videos' }
|
243
243
|
end
|
244
244
|
|
245
245
|
# Get all bookmark services
|
@@ -248,7 +248,7 @@ class Service < ActiveRecord::Base
|
|
248
248
|
# will result in the values being repopulated from the database
|
249
249
|
def self.get_bookmark_services(refresh_services = false)
|
250
250
|
@bookmark_services = nil if refresh_services
|
251
|
-
@bookmark_services ||= get_services(refresh_services).find_all{|service| service.service_category.name == 'Bookmarks' }
|
251
|
+
@bookmark_services ||= get_services(refresh_services).find_all{|service| !service.service_category.blank? && service.service_category.name == 'Bookmarks' }
|
252
252
|
end
|
253
253
|
|
254
254
|
# Get all music services
|
@@ -257,7 +257,7 @@ class Service < ActiveRecord::Base
|
|
257
257
|
# will result in the values being repopulated from the database
|
258
258
|
def self.get_music_services(refresh_services = false)
|
259
259
|
@music_services = nil if refresh_services
|
260
|
-
@music_services ||= get_services(refresh_services).find_all{|service| service.service_category.name == 'Music' }
|
260
|
+
@music_services ||= get_services(refresh_services).find_all{|service| !service.service_category.blank? && service.service_category.name == 'Music' }
|
261
261
|
end
|
262
262
|
|
263
263
|
# Get all news services
|
@@ -266,7 +266,7 @@ class Service < ActiveRecord::Base
|
|
266
266
|
# will result in the values being repopulated from the database
|
267
267
|
def self.get_news_services(refresh_services = false)
|
268
268
|
@news_services = nil if refresh_services
|
269
|
-
@news_services ||= get_services(refresh_services).find_all{|service| service.service_category.name == 'News' }
|
269
|
+
@news_services ||= get_services(refresh_services).find_all{|service| !service.service_category.blank? && service.service_category.name == 'News' }
|
270
270
|
end
|
271
271
|
|
272
272
|
# Get all blog services
|
@@ -275,7 +275,7 @@ class Service < ActiveRecord::Base
|
|
275
275
|
# will result in the values being repopulated from the database
|
276
276
|
def self.get_blog_services(refresh_services = false)
|
277
277
|
@blog_services = nil if refresh_services
|
278
|
-
@blog_services ||= get_services(refresh_services).find_all{|service| service.service_category.name == 'Blogging' }
|
278
|
+
@blog_services ||= get_services(refresh_services).find_all{|service| !service.service_category.blank? && service.service_category.name == 'Blogging' }
|
279
279
|
end
|
280
280
|
|
281
281
|
# Get all search services
|
@@ -284,7 +284,7 @@ class Service < ActiveRecord::Base
|
|
284
284
|
# will result in the values being repopulated from the database
|
285
285
|
def self.get_search_services(refresh_services = false)
|
286
286
|
@search_services = nil if refresh_services
|
287
|
-
@search_services ||= get_services(refresh_services).find_all{|service| service.service_category.name == 'Search' }
|
287
|
+
@search_services ||= get_services(refresh_services).find_all{|service| !service.service_category.blank? && service.service_category.name == 'Search' }
|
288
288
|
end
|
289
289
|
|
290
290
|
# Get all general services. These are all services except photo, video, bookmark and music.
|
@@ -304,7 +304,7 @@ class Service < ActiveRecord::Base
|
|
304
304
|
# will result in the values being repopulated from the database
|
305
305
|
def self.get_photo_tag_services(refresh_services = false)
|
306
306
|
@photo_services = nil if refresh_services
|
307
|
-
@photo_services ||= get_tag_services(nil, refresh_services).find_all{|service| service.service_category.name == 'Photos' }
|
307
|
+
@photo_services ||= get_tag_services(nil, refresh_services).find_all{|service| !service.service_category.blank? && service.service_category.name == 'Photos' }
|
308
308
|
end
|
309
309
|
|
310
310
|
# Get all video services that are used to generate tag feeds
|
@@ -313,7 +313,7 @@ class Service < ActiveRecord::Base
|
|
313
313
|
# will result in the values being repopulated from the database
|
314
314
|
def self.get_video_tag_services(refresh_services = false)
|
315
315
|
@video_services = nil if refresh_services
|
316
|
-
@video_services ||= get_tag_services(nil, refresh_services).find_all{|service| service.service_category.name == 'Videos' }
|
316
|
+
@video_services ||= get_tag_services(nil, refresh_services).find_all{|service| !service.service_category.blank? && service.service_category.name == 'Videos' }
|
317
317
|
end
|
318
318
|
|
319
319
|
# Get all bookmark services that are used to generate tag feeds
|
@@ -322,7 +322,7 @@ class Service < ActiveRecord::Base
|
|
322
322
|
# will result in the values being repopulated from the database
|
323
323
|
def self.get_bookmark_tag_services(refresh_services = false)
|
324
324
|
@bookmark_services = nil if refresh_services
|
325
|
-
@bookmark_services ||= get_tag_services(nil, refresh_services).find_all{|service| service.service_category.name == 'Bookmarks' }
|
325
|
+
@bookmark_services ||= get_tag_services(nil, refresh_services).find_all{|service| !service.service_category.blank? && service.service_category.name == 'Bookmarks' }
|
326
326
|
end
|
327
327
|
|
328
328
|
# Get all music services that are used to generate tag feeds
|
@@ -331,7 +331,7 @@ class Service < ActiveRecord::Base
|
|
331
331
|
# will result in the values being repopulated from the database
|
332
332
|
def self.get_music_tag_services(refresh_services = false)
|
333
333
|
@music_services = nil if refresh_services
|
334
|
-
@music_services ||= get_tag_services(nil, refresh_services).find_all{|service| service.service_category.name == 'Music' }
|
334
|
+
@music_services ||= get_tag_services(nil, refresh_services).find_all{|service| !service.service_category.blank? && service.service_category.name == 'Music' }
|
335
335
|
end
|
336
336
|
|
337
337
|
# Get all news services that are used to generate tag feeds
|
@@ -340,7 +340,7 @@ class Service < ActiveRecord::Base
|
|
340
340
|
# will result in the values being repopulated from the database
|
341
341
|
def self.get_news_tag_services(refresh_services = false)
|
342
342
|
@news_services = nil if refresh_services
|
343
|
-
@news_services ||= get_tag_services(nil, refresh_services).find_all{|service| service.service_category.name == 'News' }
|
343
|
+
@news_services ||= get_tag_services(nil, refresh_services).find_all{|service| !service.service_category.blank? && service.service_category.name == 'News' }
|
344
344
|
end
|
345
345
|
|
346
346
|
# Get all blog services that are used to generate tag feeds
|
@@ -349,7 +349,7 @@ class Service < ActiveRecord::Base
|
|
349
349
|
# will result in the values being repopulated from the database
|
350
350
|
def self.get_blog_tag_services(refresh_services = false)
|
351
351
|
@blog_services = nil if refresh_services
|
352
|
-
@blog_services ||= get_tag_services(nil, refresh_services).find_all{|service| service.service_category.name == 'Blogging' }
|
352
|
+
@blog_services ||= get_tag_services(nil, refresh_services).find_all{|service| !service.service_category.blank? && service.service_category.name == 'Blogging' }
|
353
353
|
end
|
354
354
|
|
355
355
|
# Get all search services that are used to generate tag feeds
|
@@ -358,7 +358,7 @@ class Service < ActiveRecord::Base
|
|
358
358
|
# will result in the values being repopulated from the database
|
359
359
|
def self.get_search_tag_services(refresh_services = false)
|
360
360
|
@search_services = nil if refresh_services
|
361
|
-
@search_services ||= get_tag_services(nil, refresh_services).find_all{|service| service.service_category.name == 'Search' }
|
361
|
+
@search_services ||= get_tag_services(nil, refresh_services).find_all{|service| !service.service_category.blank? && service.service_category.name == 'Search' }
|
362
362
|
end
|
363
363
|
|
364
364
|
# Get all general services that are used to generate tag feeds
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<%
|
2
2
|
@json_recommendations = @entry.json_recommendations(@limit, params[:order] || "mixed", true, params[:omit_feeds] || nil)
|
3
|
-
if !@json_recommendations.
|
3
|
+
if !@json_recommendations.empty?
|
4
4
|
@direct_link_text = params[:direct_link_text] || t('muck.services.direct_link')
|
5
5
|
%>
|
6
6
|
var catalog_page = <%= !@entry.direct_link.nil? and @uri == @entry.permalink %>;
|
@@ -26,6 +26,7 @@ document.write('<div class="oer_recommender_list">');
|
|
26
26
|
for(nRec = 0; nRec < recs.length; nRec++) {
|
27
27
|
r = recs[nRec].entry;
|
28
28
|
if (r == null) r = recs[nRec].attributes;
|
29
|
+
if (r == null) r = recs[nRec];
|
29
30
|
metadata_link = catalog_page && document_host == r.uri.substring(0, document_host.length);
|
30
31
|
direct_link = metadata_link && r.direct_link;
|
31
32
|
document.write('<p class="oer_recommender_item">');
|
@@ -35,11 +36,11 @@ for(nRec = 0; nRec < recs.length; nRec++) {
|
|
35
36
|
document.write(' <span class="oer_recommender_published_at">(' + format_date(r.published_at) + ')</span>');
|
36
37
|
document.write(' <span class="oer_recommender_relevance_score"><%= t('muck.services.relevance')%>: ' + Math.round(r.relevance*100)/100 + '</span>');
|
37
38
|
document.write('<br/><span class="oer_recommender_description">' + truncate(r.description) + '</span>');
|
38
|
-
document.write('<br/><span class="oer_recommender_uri">' + r.
|
39
|
+
document.write('<br/><span class="oer_recommender_uri">' + r.permalink + '</span>');
|
39
40
|
<% end -%>
|
40
41
|
document.write('</a></p>');
|
41
42
|
}
|
42
43
|
document.write('</div>');
|
43
|
-
<% if params[:more_link] %>document.write('<div class="oer_recommender_more_link"><a href="' + app + '
|
44
|
+
<% if params[:more_link] %>document.write('<div class="oer_recommender_more_link"><a href="' + app + 'resources/<%= @entry.id %>"><%= t('muck.services.gm_more_prompt') %></a></div>');<% end %>
|
44
45
|
document.write('</div>');
|
45
46
|
<% end %>
|
@@ -25,6 +25,7 @@ ActionController::Routing::Routes.draw do |map|
|
|
25
25
|
map.resources :oai_endpoints, :controller => 'muck/oai_endpoints', :has_many => :feeds
|
26
26
|
|
27
27
|
map.resources :feeds, :controller => 'muck/feeds', :collection => { :new_extended => :get, :new_oai_rss => :get }, :has_many => :entries
|
28
|
+
map.connection 'users/:login/recommendations', :controller => 'muck/personal_recommendations', :action => 'index'
|
28
29
|
|
29
30
|
map.connect 'recommendations/real_time', :controller => 'muck/recommendations', :action => 'real_time'
|
30
31
|
map.connect 'recommendations/get_button', :controller => 'muck/recommendations', :action => 'get_button'
|
@@ -11,6 +11,8 @@ module ActiveRecord
|
|
11
11
|
# +has_muck_recommendations+ gives the class it is called on personalized recommendations
|
12
12
|
def has_muck_recommendations
|
13
13
|
has_many :personal_recommendations, :as => :personal_recommendable
|
14
|
+
has_many :recommended_entries, :through => :personal_recommendations, :source => :entry,
|
15
|
+
:conditions => "personal_recommendations.destination_type = 'Entry'"
|
14
16
|
end
|
15
17
|
|
16
18
|
def acts_as_muck_recommendation
|
data/muck-services.gemspec
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{muck-services}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.24"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Joel Duffin", "Justin Ball"]
|
12
|
-
s.date = %q{2009-12-
|
12
|
+
s.date = %q{2009-12-19}
|
13
13
|
s.description = %q{This gem contains the rails specific code for dealing with feeds, aggregations and recommendations. It is meant to work with the muck-raker gem.}
|
14
14
|
s.email = %q{justin@tatemae.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -140,7 +140,6 @@ Gem::Specification.new do |s|
|
|
140
140
|
"app/views/service_templates/_zotero_group.html.erb",
|
141
141
|
"app/views/services/_edit_service.html.erb",
|
142
142
|
"app/views/services/_new_service.html.erb",
|
143
|
-
"app/views/services/_personal_recommendations.html.erb",
|
144
143
|
"app/views/services/_summary.html.erb",
|
145
144
|
"app/views/services/_view_service.html.erb",
|
146
145
|
"app/views/services_mailer/notification_feed_added.text.ar.html.erb",
|
@@ -857,4 +856,3 @@ Gem::Specification.new do |s|
|
|
857
856
|
s.add_dependency(%q<shoulda>, [">= 0"])
|
858
857
|
end
|
859
858
|
end
|
860
|
-
|
@@ -98,7 +98,7 @@ ul#add_oai_rss_feed_menu li{display:inline;margin:0 5px;}
|
|
98
98
|
#recs_header{color:#777;background-color:white;font-weight:bold;font-size:16px;font-family:Arial,Helvetica,sans-serif;margin: 2px 3px 0 3px; padding: 2px;}
|
99
99
|
#recs_list{margin: 1px; padding: 1px; list-style-type: none;}
|
100
100
|
#recs_list li{margin-left: 0px; padding: 3px; list-style-type: none; font-size: 12px;}
|
101
|
-
.even{background-color:#
|
101
|
+
.even{background-color:#DDD;}
|
102
102
|
#more_recs{margin:4px 0 6px;padding:0;text-align:center;}
|
103
103
|
#recs_panel a{color:#3987DC;text-decoration:none;}
|
104
104
|
#recs_panel a:hover{text-decoration:underline;}
|
@@ -31,6 +31,7 @@ Rails::Initializer.run do |config|
|
|
31
31
|
config.gem "geokit"
|
32
32
|
config.gem 'muck-engine', :lib => 'muck_engine'
|
33
33
|
config.gem 'muck-users', :lib => 'muck_users'
|
34
|
+
config.gem 'muck-profiles', :lib => 'muck_profiles'
|
34
35
|
config.gem 'muck-comments', :lib => 'muck_comments'
|
35
36
|
config.gem 'muck-activities', :lib => 'muck_activities'
|
36
37
|
config.gem 'muck-shares', :lib => 'muck_shares'
|
@@ -29,44 +29,10 @@ class AggregationTest < ActiveSupport::TestCase
|
|
29
29
|
should_have_many :aggregation_feeds
|
30
30
|
should_have_many :feeds
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
@first = Factory(:aggregation, :title => 'a')
|
37
|
-
@second = Factory(:aggregation, :title => 'b')
|
38
|
-
end
|
39
|
-
should "sort by title" do
|
40
|
-
assert_equal @first, Aggregation.by_title[0]
|
41
|
-
assert_equal @second, Aggregation.by_title[1]
|
42
|
-
end
|
43
|
-
end
|
44
|
-
context "recent" do
|
45
|
-
setup do
|
46
|
-
Aggregation.delete_all
|
47
|
-
@recent = Factory(:aggregation)
|
48
|
-
@not_recent = Factory(:aggregation, :created_at => 10.weeks.ago)
|
49
|
-
end
|
50
|
-
should "get recent aggregations" do
|
51
|
-
assert Aggregation.recent.include?(@recent)
|
52
|
-
end
|
53
|
-
should "not get recent aggregations" do
|
54
|
-
assert !Aggregation.recent.include?(@not_recent)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
context "newest" do
|
58
|
-
setup do
|
59
|
-
Aggregation.delete_all
|
60
|
-
@first = Factory(:aggregation, :created_at => 1.day.ago)
|
61
|
-
@second = Factory(:aggregation, :created_at => 1.week.ago)
|
62
|
-
end
|
63
|
-
should "sort by created_at" do
|
64
|
-
assert_equal @first, Aggregation.newest[0]
|
65
|
-
assert_equal @second, Aggregation.newest[1]
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
32
|
+
should_scope_by_title
|
33
|
+
should_scope_recent
|
34
|
+
should_scope_newest
|
35
|
+
|
70
36
|
context "filter feed types" do
|
71
37
|
setup do
|
72
38
|
build_music_service
|
@@ -53,11 +53,9 @@ class FeedTest < ActiveSupport::TestCase
|
|
53
53
|
|
54
54
|
should_validate_presence_of :uri
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
should_have_named_scope :by_title
|
60
|
-
should_have_named_scope :recent
|
56
|
+
should_scope_by_title
|
57
|
+
should_scope_recent
|
58
|
+
should_scope_by_newest
|
61
59
|
|
62
60
|
should "set 24 hours as default interval" do
|
63
61
|
assert_equal @feed.harvest_interval_hours, 24
|
@@ -68,6 +66,35 @@ class FeedTest < ActiveSupport::TestCase
|
|
68
66
|
assert_equal @feed.harvest_interval, 10 * 3600
|
69
67
|
end
|
70
68
|
|
69
|
+
context "named scope" do
|
70
|
+
context "banned" do
|
71
|
+
# named_scope :banned, :conditions => ["status = ?", MuckServices::Status::BANNED]
|
72
|
+
setup do
|
73
|
+
@feed = Factory(:feed, :status => MuckServices::Status::BANNED)
|
74
|
+
@feed_not = Factory(:feed)
|
75
|
+
end
|
76
|
+
should "find feeds that are banned" do
|
77
|
+
assert Feed.banned.include?(@feed)
|
78
|
+
end
|
79
|
+
should "not find feeds that are not banned" do
|
80
|
+
assert !Feed.banned.include?(@feed_not)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
context "valid" do
|
84
|
+
# named_scope :valid, :conditions => "status >= 0", :include => [:default_language]
|
85
|
+
setup do
|
86
|
+
@feed = Factory(:feed, :status => 0)
|
87
|
+
@feed_not = Factory(:feed, :status => MuckServices::Status::BANNED)
|
88
|
+
end
|
89
|
+
should "find valid feeds" do
|
90
|
+
assert Feed.valid.include?(@feed)
|
91
|
+
end
|
92
|
+
should "not find invalid feeds" do
|
93
|
+
assert !Feed.valid.include?(@feed_not)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
71
98
|
end
|
72
99
|
|
73
100
|
context "Feed status" do
|
@@ -32,25 +32,52 @@ class OaiEndpointTest < ActiveSupport::TestCase
|
|
32
32
|
|
33
33
|
should_validate_presence_of :uri
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
35
|
+
should_scope_by_title
|
36
|
+
should_scope_recent
|
37
|
+
should_scope_by_newest
|
38
|
+
|
39
|
+
context "named scope" do
|
40
|
+
context "banned" do
|
41
|
+
# named_scope :banned, :conditions => ["status = ?", MuckServices::Status::BANNED]
|
42
|
+
setup do
|
43
|
+
@oai_endpoint = Factory(:oai_endpoint, :status => MuckServices::Status::BANNED)
|
44
|
+
@oai_endpoint_not = Factory(:oai_endpoint)
|
45
|
+
end
|
46
|
+
should "find oai_endpoints that are banned" do
|
47
|
+
assert OaiEndpoint.banned.include?(@oai_endpoint)
|
48
|
+
end
|
49
|
+
should "not find oai_endpoints that are not banned" do
|
50
|
+
assert !OaiEndpoint.banned.include?(@oai_endpoint_not)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
context "valid" do
|
54
|
+
# named_scope :valid, :conditions => "status >= 0", :include => [:default_language]
|
55
|
+
setup do
|
56
|
+
@oai_endpoint = Factory(:oai_endpoint, :status => 0)
|
57
|
+
@oai_endpoint_not = Factory(:oai_endpoint, :status => MuckServices::Status::BANNED)
|
58
|
+
end
|
59
|
+
should "find valid oai_endpoints" do
|
60
|
+
assert OaiEndpoint.valid.include?(@oai_endpoint)
|
61
|
+
end
|
62
|
+
should "not find invalid oai_endpoints" do
|
63
|
+
assert !OaiEndpoint.valid.include?(@oai_endpoint_not)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
40
67
|
|
41
68
|
end
|
42
69
|
|
43
70
|
context "banned/unbanned" do
|
44
71
|
setup do
|
45
|
-
@
|
72
|
+
@oai_endpoint = Factory(:oai_endpoint)
|
46
73
|
end
|
47
74
|
should "be banned" do
|
48
|
-
@
|
49
|
-
assert @
|
75
|
+
@oai_endpoint.status = -1
|
76
|
+
assert @oai_endpoint.banned?
|
50
77
|
end
|
51
78
|
should "not be banned" do
|
52
|
-
@
|
53
|
-
assert !@
|
79
|
+
@oai_endpoint.status = 0
|
80
|
+
assert !@oai_endpoint.banned?
|
54
81
|
end
|
55
82
|
end
|
56
83
|
|
@@ -24,11 +24,64 @@ require File.dirname(__FILE__) + '/../test_helper'
|
|
24
24
|
class ServiceTest < ActiveSupport::TestCase
|
25
25
|
|
26
26
|
context "service instance" do
|
27
|
-
|
27
|
+
|
28
28
|
should_belong_to :service_category
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
should_scope_sorted
|
30
|
+
|
31
|
+
context "named scope" do
|
32
|
+
context "identity_services" do
|
33
|
+
# named_scope :identity_services, :conditions => ['use_for = ?', 'identity']
|
34
|
+
setup do
|
35
|
+
@service = Factory(:service, :use_for => 'identity')
|
36
|
+
@service_not = Factory(:service, :use_for => 'not')
|
37
|
+
end
|
38
|
+
should "find services that have use_for=='identity_services'" do
|
39
|
+
assert Service.identity_services.include?(@service)
|
40
|
+
end
|
41
|
+
should "not find services that where use_for!='identity_services'" do
|
42
|
+
assert !Service.identity_services.include?(@service_not)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
context "tag_services" do
|
46
|
+
# named_scope :tag_services, :conditions => ['use_for = ?', 'tags']
|
47
|
+
setup do
|
48
|
+
@service = Factory(:service, :use_for => 'tags')
|
49
|
+
@service_not = Factory(:service, :use_for => 'not')
|
50
|
+
end
|
51
|
+
should "find services that have use_for=='tags'" do
|
52
|
+
assert Service.tag_services.include?(@service)
|
53
|
+
end
|
54
|
+
should "not find services that where use_for!='tags'" do
|
55
|
+
assert !Service.tag_services.include?(@service_not)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
context "sorted_id" do
|
59
|
+
# named_scope :sorted_id, :order => "id ASC"
|
60
|
+
setup do
|
61
|
+
Service.delete_all
|
62
|
+
@first = Factory(:service)
|
63
|
+
@second = Factory(:service)
|
64
|
+
end
|
65
|
+
should "sort by 'sort' field" do
|
66
|
+
assert_equal @first, Service.sorted_id[0]
|
67
|
+
assert_equal @second, Service.sorted_id[1]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
context "photo_services" do
|
71
|
+
# named_scope :photo_services, :conditions => ["service_categories.id = services.service_category_id AND service_categories.name = 'Photos'"], :include => ['service_category']
|
72
|
+
setup do
|
73
|
+
@service_category = Factory(:service_category, :name => 'Photos')
|
74
|
+
@service = Factory(:service, :service_category => @service_category)
|
75
|
+
@service_not = Factory(:service)
|
76
|
+
end
|
77
|
+
should "find services that have 'Photos' for a service category" do
|
78
|
+
assert Service.photo_services.include?(@service)
|
79
|
+
end
|
80
|
+
should "not find services that don't have 'Photos' for a service category" do
|
81
|
+
assert !Service.photo_services.include?(@service_not)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
32
85
|
|
33
86
|
context "photos" do
|
34
87
|
setup do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: muck-services
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.24
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joel Duffin
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2009-12-
|
13
|
+
date: 2009-12-19 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -266,7 +266,6 @@ files:
|
|
266
266
|
- app/views/service_templates/_zotero_group.html.erb
|
267
267
|
- app/views/services/_edit_service.html.erb
|
268
268
|
- app/views/services/_new_service.html.erb
|
269
|
-
- app/views/services/_personal_recommendations.html.erb
|
270
269
|
- app/views/services/_summary.html.erb
|
271
270
|
- app/views/services/_view_service.html.erb
|
272
271
|
- app/views/services_mailer/notification_feed_added.text.ar.html.erb
|