recommendations 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/recommendations/item/similarity.rb +22 -0
- data/lib/recommendations/item.rb +2 -18
- data/lib/recommendations/user/ratings.rb +13 -0
- data/lib/recommendations/user.rb +36 -0
- data/lib/recommendations/version.rb +1 -1
- data/lib/recommendations.rb +3 -0
- data/spec/recommendations/item_spec.rb +32 -0
- data/spec/recommendations/user/ratings_spec.rb +32 -0
- data/spec/recommendations/user_spec.rb +25 -0
- data/spec/spec_helper.rb +7 -0
- metadata +15 -6
@@ -0,0 +1,22 @@
|
|
1
|
+
module Recommendations
|
2
|
+
class Item
|
3
|
+
module Similarity
|
4
|
+
def similars(count = Recommendations.configuration.similar_count)
|
5
|
+
Recommendations.redis.zrange("recommendations:similars:#{id}", 0, count - 1)
|
6
|
+
end
|
7
|
+
|
8
|
+
def update_similars
|
9
|
+
Recommendations.redis.del("recommendations:similars:#{id}")
|
10
|
+
|
11
|
+
categories.each do |category, score|
|
12
|
+
items = Recommendations.redis.hgetall("recommendations:categories:#{category}")
|
13
|
+
|
14
|
+
items.reject {|k, v| k == id}.each do |item, item_score|
|
15
|
+
difference = (Float(item_score) - Float(score)).abs
|
16
|
+
Recommendations.redis.zincrby("recommendations:similars:#{id}", difference, item)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/recommendations/item.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
module Recommendations
|
2
2
|
class Item
|
3
|
+
include Item::Similarity
|
3
4
|
attr_accessor :id
|
4
5
|
attr_writer :categories
|
5
6
|
|
6
|
-
def initialize(id, categories)
|
7
|
+
def initialize(id, categories = {})
|
7
8
|
@id, @categories = id, categories
|
8
9
|
end
|
9
10
|
|
@@ -11,23 +12,6 @@ module Recommendations
|
|
11
12
|
@categories || {}
|
12
13
|
end
|
13
14
|
|
14
|
-
def similars(count = Recommendations.configuration.similar_count)
|
15
|
-
Recommendations.redis.zrange("recommendations:similars:#{id}", 0, count - 1)
|
16
|
-
end
|
17
|
-
|
18
|
-
def update_similars
|
19
|
-
Recommendations.redis.del("recommendations:similars:#{id}")
|
20
|
-
|
21
|
-
categories.each do |category, score|
|
22
|
-
items = Recommendations.redis.hgetall("recommendations:categories:#{category}")
|
23
|
-
|
24
|
-
items.reject {|k, v| k == id}.each do |item, item_score|
|
25
|
-
difference = (Float(item_score) - Float(score)).abs
|
26
|
-
Recommendations.redis.zincrby("recommendations:similars:#{id}", difference, item)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
15
|
def save!
|
32
16
|
Recommendations.redis.hmset("recommendations:item:#{id}", *categories.flatten)
|
33
17
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Recommendations
|
2
|
+
class User
|
3
|
+
module Ratings
|
4
|
+
def rate(item_id, rating)
|
5
|
+
Recommendations.redis.hset("recommendations:ratings:#{id}", item_id, rating)
|
6
|
+
end
|
7
|
+
|
8
|
+
def ratings
|
9
|
+
Recommendations.redis.hgetall("recommendations:ratings:#{id}")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Recommendations
|
2
|
+
MAX_VAL = 2
|
3
|
+
|
4
|
+
class User
|
5
|
+
include User::Ratings
|
6
|
+
|
7
|
+
attr_accessor :id
|
8
|
+
|
9
|
+
def initialize(id)
|
10
|
+
@id = id
|
11
|
+
end
|
12
|
+
|
13
|
+
def recommendations
|
14
|
+
Recommendations.redis.hgetall("recommendations:user:#{id}")
|
15
|
+
end
|
16
|
+
|
17
|
+
def update_recommendations
|
18
|
+
ratings = self.ratings
|
19
|
+
|
20
|
+
recommendations = ratings.inject({}) do |buffer, (item_id, score)|
|
21
|
+
similars = Item.find(item_id).similars
|
22
|
+
similars.reject! {|i| ratings.key?(i)}
|
23
|
+
|
24
|
+
similars.each do |similar|
|
25
|
+
buffer[similar] ||= 0
|
26
|
+
buffer[similar] += (Float(score) - 3) # 0..5 score => -2..2
|
27
|
+
end
|
28
|
+
|
29
|
+
buffer
|
30
|
+
end
|
31
|
+
|
32
|
+
Recommendations.redis.hmset("recommendations:user:#{id}", *recommendations.flatten)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
data/lib/recommendations.rb
CHANGED
@@ -2,6 +2,9 @@ require "redis"
|
|
2
2
|
|
3
3
|
require "recommendations/version"
|
4
4
|
require "recommendations/configuration"
|
5
|
+
require "recommendations/user/ratings"
|
6
|
+
require "recommendations/user"
|
7
|
+
require "recommendations/item/similarity"
|
5
8
|
require "recommendations/item"
|
6
9
|
|
7
10
|
module Recommendations
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Recommendations::Item do
|
4
|
+
subject {Recommendations::Item.new("1", violent: 100, romantic: 25)}
|
5
|
+
|
6
|
+
describe "#initialize" do
|
7
|
+
its(:id) {should == "1"}
|
8
|
+
its(:categories) {should == {violent: 100, romantic: 25}}
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#similars" do
|
12
|
+
context "when count was not specified" do
|
13
|
+
let(:count) {Recommendations.configuration.similar_count - 1}
|
14
|
+
|
15
|
+
it "gets sorted set range from similar count configuration" do
|
16
|
+
Recommendations.redis.should_receive(:zrange)
|
17
|
+
.with("recommendations:similars:1", 0, count)
|
18
|
+
.and_return ["2", "3", "4"]
|
19
|
+
subject.similars.should == ["2", "3", "4"]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "when count was specified" do
|
24
|
+
it "gets sorted set range until count" do
|
25
|
+
Recommendations.redis.should_receive(:zrange)
|
26
|
+
.with("recommendations:similars:1", 0, 9)
|
27
|
+
.and_return ["2", "3", "4"]
|
28
|
+
subject.similars(10).should == ["2", "3", "4"]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Recommendations::User::Ratings do
|
4
|
+
subject {Recommendations::User.new("1")}
|
5
|
+
|
6
|
+
describe "#rate" do
|
7
|
+
it "stores a user rating for this item" do
|
8
|
+
subject.rate("890", 4.5)
|
9
|
+
Recommendations.redis.hget("recommendations:ratings:1", "890").should == "4.5"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#ratings" do
|
14
|
+
let :ratings do
|
15
|
+
{
|
16
|
+
"1" => "5",
|
17
|
+
"2" => "4",
|
18
|
+
"3" => "3",
|
19
|
+
"4" => "2",
|
20
|
+
"5" => "1"
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
before do
|
25
|
+
ratings.each {|k, v| subject.rate(k, v)}
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns all ratings" do
|
29
|
+
subject.ratings.should == ratings
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Recommendations::User do
|
4
|
+
describe "#update_recommendations" do
|
5
|
+
subject {Recommendations::User.new("1")}
|
6
|
+
|
7
|
+
before do
|
8
|
+
i1 = Recommendations::Item.new("1", violento: 100, romantico: 0, acao: 50)
|
9
|
+
i2 = Recommendations::Item.new("2", violento: 50, romantico: 0, acao: 50) # 1.50
|
10
|
+
i3 = Recommendations::Item.new("3", violento: 100, romantico: 100, acao: 0) # 1.150, 2.200
|
11
|
+
i4 = Recommendations::Item.new("4", violento: 70, romantico: 30, acao: 20) # 1.90, 2.80
|
12
|
+
|
13
|
+
items = [i1, i2, i3, i4]
|
14
|
+
items.each(&:save!)
|
15
|
+
items.each(&:update_similars)
|
16
|
+
|
17
|
+
subject.rate("1", 5)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "updates recommendations" do
|
21
|
+
subject.update_recommendations
|
22
|
+
subject.recommendations.should == {"2" => "2.0", "4" => "2.0", "3" => "2.0"}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: recommendations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-05-
|
12
|
+
date: 2012-05-18 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &70235365846640 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '2.10'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70235365846640
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: redis
|
27
|
-
requirement: &
|
27
|
+
requirement: &70235365823420 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70235365823420
|
36
36
|
description: recommendations between similar items
|
37
37
|
email:
|
38
38
|
- victorc.rodrigues@gmail.com
|
@@ -50,8 +50,14 @@ files:
|
|
50
50
|
- lib/recommendations.rb
|
51
51
|
- lib/recommendations/configuration.rb
|
52
52
|
- lib/recommendations/item.rb
|
53
|
+
- lib/recommendations/item/similarity.rb
|
54
|
+
- lib/recommendations/user.rb
|
55
|
+
- lib/recommendations/user/ratings.rb
|
53
56
|
- lib/recommendations/version.rb
|
54
57
|
- recommendations.gemspec
|
58
|
+
- spec/recommendations/item_spec.rb
|
59
|
+
- spec/recommendations/user/ratings_spec.rb
|
60
|
+
- spec/recommendations/user_spec.rb
|
55
61
|
- spec/recommendations_spec.rb
|
56
62
|
- spec/spec_helper.rb
|
57
63
|
homepage: ''
|
@@ -79,5 +85,8 @@ signing_key:
|
|
79
85
|
specification_version: 3
|
80
86
|
summary: ''
|
81
87
|
test_files:
|
88
|
+
- spec/recommendations/item_spec.rb
|
89
|
+
- spec/recommendations/user/ratings_spec.rb
|
90
|
+
- spec/recommendations/user_spec.rb
|
82
91
|
- spec/recommendations_spec.rb
|
83
92
|
- spec/spec_helper.rb
|