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.
@@ -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
@@ -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
+
@@ -1,3 +1,3 @@
1
1
  module Recommendations
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -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
@@ -1 +1,8 @@
1
1
  require "rspec"
2
+ require "recommendations"
3
+
4
+ RSpec.configure do |config|
5
+ config.before do
6
+ Recommendations.redis.flushdb
7
+ end
8
+ end
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.1
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-16 00:00:00.000000000Z
12
+ date: 2012-05-18 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &70243608433720 !ruby/object:Gem::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: *70243608433720
24
+ version_requirements: *70235365846640
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: redis
27
- requirement: &70243608433280 !ruby/object:Gem::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: *70243608433280
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