recommendations 0.0.1 → 0.0.2
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/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
|