Commit c6410e3e4be39e5dc54cda1d396faaa8f3d069d6
1 parent
fd9ec40e
Exists in
master
and in
27 other branches
profile-suggestions: connections
Showing
5 changed files
with
88 additions
and
67 deletions
Show diff stats
app/models/profile_suggestion.rb
... | ... | @@ -4,6 +4,10 @@ class ProfileSuggestion < ActiveRecord::Base |
4 | 4 | |
5 | 5 | attr_accessible :person, :suggestion, :suggestion_type, :categories, :enabled |
6 | 6 | |
7 | + has_many :suggestion_connections, :foreign_key => 'suggestion_id' | |
8 | + has_many :profile_connections, :through => :suggestion_connections, :source => :connection, :source_type => 'Profile' | |
9 | + has_many :tag_connections, :through => :suggestion_connections, :source => :connection, :source_type => 'ActsAsTaggableOn::Tag' | |
10 | + | |
7 | 11 | before_create do |profile_suggestion| |
8 | 12 | profile_suggestion.suggestion_type = self.suggestion.class.to_s |
9 | 13 | end |
... | ... | @@ -45,19 +49,19 @@ class ProfileSuggestion < ActiveRecord::Base |
45 | 49 | |
46 | 50 | RULES = { |
47 | 51 | :people_with_common_communities => { |
48 | - :threshold => 2, :weight => 1 | |
52 | + :threshold => 2, :weight => 1, :connection => 'Profile' | |
49 | 53 | }, |
50 | 54 | :people_with_common_friends => { |
51 | - :threshold => 2, :weight => 1 | |
55 | + :threshold => 2, :weight => 1, :connection => 'Profile' | |
52 | 56 | }, |
53 | 57 | :people_with_common_tags => { |
54 | - :threshold => 2, :weight => 1 | |
58 | + :threshold => 2, :weight => 1, :connection => 'ActsAsTaggableOn::Tag' | |
55 | 59 | }, |
56 | 60 | :communities_with_common_friends => { |
57 | - :threshold => 2, :weight => 1 | |
61 | + :threshold => 2, :weight => 1, :connection => 'Profile' | |
58 | 62 | }, |
59 | 63 | :communities_with_common_tags => { |
60 | - :threshold => 2, :weight => 1 | |
64 | + :threshold => 2, :weight => 1, :connection => 'ActsAsTaggableOn::Tag' | |
61 | 65 | } |
62 | 66 | } |
63 | 67 | |
... | ... | @@ -118,9 +122,21 @@ class ProfileSuggestion < ActiveRecord::Base |
118 | 122 | suggested_profiles.each do |suggested_profile| |
119 | 123 | suggestion = person.profile_suggestions.find_or_initialize_by_suggestion_id(suggested_profile.id) |
120 | 124 | RULES.each do |rule, options| |
121 | - value = suggested_profile.send("#{rule}_count").to_i | |
125 | + begin | |
126 | + value = suggested_profile.send("#{rule}_count").to_i | |
127 | + rescue NoMethodError | |
128 | + next | |
129 | + end | |
130 | + connections = suggested_profile.send("#{rule}_connections") | |
131 | + if connections.present? | |
132 | + connections = connections[1..-2].split(',') | |
133 | + else | |
134 | + connections = [] | |
135 | + end | |
122 | 136 | suggestion.send("#{rule}=", value) |
123 | - #TODO Create suggestion connections. | |
137 | + connections.each do |connection_id| | |
138 | + SuggestionConnection.create!(:suggestion => suggestion, :connection_id => connection_id, :connection_type => options[:connection]) | |
139 | + end | |
124 | 140 | suggestion.score += value * options[:weight] |
125 | 141 | end |
126 | 142 | suggestion.save! |
... | ... | @@ -202,15 +218,25 @@ class ProfileSuggestion < ActiveRecord::Base |
202 | 218 | end |
203 | 219 | |
204 | 220 | def self.all_suggestions(person) |
205 | - select_string = "profiles.*, " + RULES.keys.map { |rule| "suggestions.#{counter(rule)} as #{counter(rule)}, suggestions.#{connections(rule)} as #{connections(rule)}" }.join(',') | |
221 | + select_string = ["profiles.*"] | |
222 | + suggestions_join = [] | |
223 | + where_string = [] | |
224 | + valid_rules = [] | |
206 | 225 | previous_rule = nil |
207 | - suggestions_join = RULES.keys.map do |rule| | |
226 | + join_column = nil | |
227 | + RULES.each do |rule, options| | |
228 | + rule_select = self.send(rule, person) | |
229 | + next if !rule_select.present? | |
230 | + | |
231 | + valid_rules << rule | |
232 | + select_string << "suggestions.#{counter(rule)} as #{counter(rule)}, suggestions.#{connections(rule)} as #{connections(rule)}" | |
233 | + where_string << "#{counter(rule)} >= #{options[:threshold]}" | |
208 | 234 | rule_select = " |
209 | 235 | (SELECT profiles.id as #{profile_id(rule)}, |
210 | 236 | #{rule}_sub.#{counter(rule)} as #{counter(rule)}, |
211 | 237 | #{rule}_sub.#{connections(rule)} as #{connections(rule)} |
212 | 238 | FROM profiles |
213 | - LEFT OUTER JOIN (#{self.send(rule, person)}) as #{rule}_sub | |
239 | + LEFT OUTER JOIN (#{rule_select}) as #{rule}_sub | |
214 | 240 | ON profiles.id = #{rule}_sub.#{profile_id(rule)}) AS #{rule}" |
215 | 241 | |
216 | 242 | if previous_rule.nil? |
... | ... | @@ -220,10 +246,14 @@ class ProfileSuggestion < ActiveRecord::Base |
220 | 246 | ON #{previous_rule}.#{profile_id(previous_rule)} = #{rule}.#{profile_id(rule)}" |
221 | 247 | end |
222 | 248 | previous_rule = rule |
223 | - result | |
224 | - end.join(' ') | |
225 | - join_string = "INNER JOIN (SELECT * FROM #{suggestions_join}) AS suggestions ON profiles.id = suggestions.#{profile_id(RULES.keys.first)}" | |
226 | - where_string = RULES.map { |rule, options| "#{counter(rule)} >= #{options[:threshold]}"}.join(' OR ') | |
249 | + suggestions_join << result | |
250 | + end | |
251 | + | |
252 | + return if valid_rules.blank? | |
253 | + | |
254 | + select_string = select_string.compact.join(',') | |
255 | + join_string = "INNER JOIN (SELECT * FROM #{suggestions_join.compact.join(' ')}) AS suggestions ON profiles.id = suggestions.#{profile_id(valid_rules.first)}" | |
256 | + where_string = where_string.compact.join(' OR ') | |
227 | 257 | |
228 | 258 | person.environment.profiles. |
229 | 259 | select(select_string). | ... | ... |
... | ... | @@ -0,0 +1,6 @@ |
1 | +class SuggestionConnection < ActiveRecord::Base | |
2 | + attr_accessible :suggestion, :connection_type, :connection_id | |
3 | + | |
4 | + belongs_to :suggestion, :class_name => 'ProfileSuggestion', :foreign_key => 'suggestion_id' | |
5 | + belongs_to :connection, :polymorphic => true | |
6 | +end | ... | ... |
db/migrate/20140811141211_create_suggestion_connections.rb
0 → 100644
... | ... | @@ -0,0 +1,12 @@ |
1 | +class CreateSuggestionConnections < ActiveRecord::Migration | |
2 | + def up | |
3 | + create_table :suggestion_connections do |t| | |
4 | + t.references :suggestion, :null => false | |
5 | + t.references :connection, :polymorphic => true, :null => false | |
6 | + end | |
7 | + end | |
8 | + | |
9 | + def down | |
10 | + drop_table :suggestion_connections | |
11 | + end | |
12 | +end | ... | ... |
db/schema.rb
... | ... | @@ -11,7 +11,7 @@ |
11 | 11 | # |
12 | 12 | # It's strongly recommended to check this file into your version control system. |
13 | 13 | |
14 | -ActiveRecord::Schema.define(:version => 20140805205626) do | |
14 | +ActiveRecord::Schema.define(:version => 20140811141211) do | |
15 | 15 | |
16 | 16 | create_table "abuse_reports", :force => true do |t| |
17 | 17 | t.integer "reporter_id" |
... | ... | @@ -603,6 +603,12 @@ ActiveRecord::Schema.define(:version => 20140805205626) do |
603 | 603 | add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id" |
604 | 604 | add_index "sessions", ["updated_at"], :name => "index_sessions_on_updated_at" |
605 | 605 | |
606 | + create_table "suggestion_connections", :force => true do |t| | |
607 | + t.integer "suggestion_id", :null => false | |
608 | + t.integer "connection_id", :null => false | |
609 | + t.string "connection_type", :null => false | |
610 | + end | |
611 | + | |
606 | 612 | create_table "taggings", :force => true do |t| |
607 | 613 | t.integer "tag_id" |
608 | 614 | t.integer "taggable_id" | ... | ... |
test/unit/profile_suggestion_test.rb
... | ... | @@ -57,7 +57,10 @@ class ProfileSuggestionTest < ActiveSupport::TestCase |
57 | 57 | p4.add_friend(p6) ; p6.add_friend(p4) |
58 | 58 | p2.add_friend(p7) ; p7.add_friend(p2) |
59 | 59 | |
60 | - assert_equivalent ProfileSuggestion.people_with_common_friends(p1), [p5,p6] | |
60 | + suggestions = ProfileSuggestion.calculate_suggestions(p1) | |
61 | + | |
62 | + assert_includes suggestions, p5 | |
63 | + assert_includes suggestions, p6 | |
61 | 64 | end |
62 | 65 | |
63 | 66 | should 'calculate people with common_communities' do |
... | ... | @@ -81,7 +84,10 @@ class ProfileSuggestionTest < ActiveSupport::TestCase |
81 | 84 | c3.add_member(p4) |
82 | 85 | c4.add_member(p5) |
83 | 86 | |
84 | - assert_equivalent ProfileSuggestion.people_with_common_communities(p1), [p2,p4] | |
87 | + suggestions = ProfileSuggestion.calculate_suggestions(p1) | |
88 | + | |
89 | + assert_includes suggestions, p2 | |
90 | + assert_includes suggestions, p4 | |
85 | 91 | end |
86 | 92 | |
87 | 93 | should 'calculate people with common_tags' do |
... | ... | @@ -121,7 +127,10 @@ class ProfileSuggestionTest < ActiveSupport::TestCase |
121 | 127 | a52.tag_list = ['onivorism', 'facism'] |
122 | 128 | a52.save! |
123 | 129 | |
124 | - assert_equivalent ProfileSuggestion.people_with_common_tags(p1), [p2, p3] | |
130 | + suggestions = ProfileSuggestion.calculate_suggestions(p1) | |
131 | + | |
132 | + assert_includes suggestions, p2 | |
133 | + assert_includes suggestions, p3 | |
125 | 134 | end |
126 | 135 | |
127 | 136 | should 'calculate communities with common_friends' do |
... | ... | @@ -146,7 +155,10 @@ class ProfileSuggestionTest < ActiveSupport::TestCase |
146 | 155 | c3.add_member(p2) |
147 | 156 | c4.add_member(p3) |
148 | 157 | |
149 | - assert_equivalent ProfileSuggestion.communities_with_common_friends(p1), [c1,c2] | |
158 | + suggestions = ProfileSuggestion.calculate_suggestions(p1) | |
159 | + | |
160 | + assert_includes suggestions, c1 | |
161 | + assert_includes suggestions, c2 | |
150 | 162 | end |
151 | 163 | |
152 | 164 | should 'calculate communities with common_tags' do |
... | ... | @@ -186,39 +198,10 @@ class ProfileSuggestionTest < ActiveSupport::TestCase |
186 | 198 | a52.tag_list = ['onivorism', 'facism'] |
187 | 199 | a52.save! |
188 | 200 | |
189 | - assert_equivalent ProfileSuggestion.communities_with_common_tags(p1), [p2, p3] | |
190 | - end | |
201 | + suggestions = ProfileSuggestion.calculate_suggestions(p1) | |
191 | 202 | |
192 | - should 'register suggestions' do | |
193 | - person = create_user('person').person | |
194 | - rule = ProfileSuggestion::RULES.sample | |
195 | - p1 = fast_create(Profile) | |
196 | - p2 = fast_create(Profile) | |
197 | - p3 = fast_create(Profile) | |
198 | - # Hack to simulate a common_count that generated on the rules | |
199 | - suggestions = Profile.select('profiles.*, profiles.id as common_count').where("id in (#{[p1,p2,p3].map(&:id).join(',')})") | |
200 | - | |
201 | - assert_difference 'ProfileSuggestion.count', 3 do | |
202 | - ProfileSuggestion.register_suggestions(person, suggestions, rule) | |
203 | - end | |
204 | - assert_no_difference 'ProfileSuggestion.count' do | |
205 | - s1 = ProfileSuggestion.find_or_initialize_by_suggestion_id(p1.id) | |
206 | - assert_equal p1, s1.suggestion | |
207 | - s2 = ProfileSuggestion.find_or_initialize_by_suggestion_id(p2.id) | |
208 | - assert_equal p2, s2.suggestion | |
209 | - s3 = ProfileSuggestion.find_or_initialize_by_suggestion_id(p3.id) | |
210 | - assert_equal p3, s3.suggestion | |
211 | - end | |
212 | - end | |
213 | - | |
214 | - should 'calculate every rule suggestion' do | |
215 | - person = create_user('person').person | |
216 | - ProfileSuggestion::RULES.each do |rule| | |
217 | - suggestion = fast_create(Profile) | |
218 | - ProfileSuggestion.expects(rule).returns([suggestion]) | |
219 | - ProfileSuggestion.expects(:register_suggestions).with(person, [suggestion], rule).returns(true) | |
220 | - end | |
221 | - ProfileSuggestion.calculate_suggestions(person) | |
203 | + assert_includes suggestions, p2 | |
204 | + assert_includes suggestions, p3 | |
222 | 205 | end |
223 | 206 | |
224 | 207 | #FIXME This might not be necessary anymore... |
... | ... | @@ -303,22 +286,6 @@ class ProfileSuggestionTest < ActiveSupport::TestCase |
303 | 286 | # end |
304 | 287 | # end |
305 | 288 | |
306 | - should 'register only new suggestions' do | |
307 | - person = create_user('person').person | |
308 | - ProfileSuggestion::SUGGESTIONS_BY_RULE.times do | |
309 | - ProfileSuggestion.create!(:person => person, :suggestion => fast_create(Person)) | |
310 | - end | |
311 | - | |
312 | - person.reload | |
313 | - new_suggestion = fast_create(Person) | |
314 | - ids = (person.suggested_people + [new_suggestion]).map(&:id).join(',') | |
315 | - suggested_profiles = Profile.select('profiles.*, profiles.id as common_count').where("profiles.id IN (#{ids})") | |
316 | - | |
317 | - assert_difference 'ProfileSuggestion.count', 1 do | |
318 | - ProfileSuggestion.register_suggestions(person, suggested_profiles, 'people_with_common_friends') | |
319 | - end | |
320 | - end | |
321 | - | |
322 | 289 | should 'calculate new suggestions when number of available suggestions reaches the min_limit' do |
323 | 290 | person = create_user('person').person |
324 | 291 | (ProfileSuggestion::MIN_LIMIT + 1).times do | ... | ... |