Commit d3025fcfb3df15cc3fdd0fb86486895995f5da6f

Authored by Hugo Melo
1 parent 09d49988

Adapt gamification to profile categorization

 * Add point type and point categorization
 * Adapt point rules for new models
 * Adapt existing tests
 * Create a seeds file
db/migrate/20150924010919_create_gamification_plugin_points_types.rb 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +class CreateGamificationPluginPointsTypes < ActiveRecord::Migration
  2 + def change
  3 + create_table :gamification_plugin_points_types do |t|
  4 + t.string :name
  5 + t.text :description
  6 +
  7 + t.timestamps
  8 + end
  9 + end
  10 +end
... ...
db/migrate/20150924011409_create_gamification_plugin_points_categorizations.rb 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +class CreateGamificationPluginPointsCategorizations < ActiveRecord::Migration
  2 + def change
  3 + create_table :gamification_plugin_points_categorizations do |t|
  4 + t.references :profile
  5 + t.integer :point_type_id
  6 + t.integer :weight
  7 +
  8 + t.timestamps
  9 + end
  10 + add_index :gamification_plugin_points_categorizations, :profile_id
  11 + add_index :gamification_plugin_points_categorizations, :point_type_id, name: 'index_points_categorizations_on_point_type_id'
  12 + end
  13 +end
... ...
db/seeds.rb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +unless GamificationPlugin::PointsType.count
  2 + Merit::PointRules::AVAILABLE_RULES.each do |name , setting|
  3 + GamificationPlugin::PointsType.create! name: name.to_s, description: setting[:description]
  4 + end
  5 +end
... ...
lib/ext/person.rb
... ... @@ -9,6 +9,7 @@ class Person
9 9 self.points(category: 'profile_completion') == 0 and self.is_profile_complete?
10 10 end
11 11 def is_profile_complete?
12   - true
  12 + # FIXME: FIND OUT A WAY TO CHECK EVERY REGISTRY FIELD
  13 + false
13 14 end
14 15 end
... ...
lib/merit/point_rules.rb
... ... @@ -9,7 +9,8 @@ module Merit
9 9 to: :author,
10 10 value: 1,
11 11 description: _('Comment author'),
12   - default_weight: 150
  12 + default_weight: 150,
  13 + condition: lambda {|comment, profile| comment.source.profile == profile},
13 14 },
14 15 comment_article_author: {
15 16 action: 'comment#create',
... ... @@ -17,7 +18,8 @@ module Merit
17 18 to: lambda {|comment| comment.source.author},
18 19 value: 1,
19 20 description: _('Article author of a comment'),
20   - default_weight: 50
  21 + default_weight: 50,
  22 + condition: lambda {|comment, profile| comment.source.profile == profile},
21 23 },
22 24 comment_article: {
23 25 action: 'comment#create',
... ... @@ -25,7 +27,8 @@ module Merit
25 27 to: lambda {|comment| comment.source},
26 28 value: 1,
27 29 description: _('Source article of a comment'),
28   - default_weight: 50
  30 + default_weight: 50,
  31 + condition: lambda {|comment, profile| comment.source.profile == profile},
29 32 },
30 33 comment_community: {
31 34 action: 'comment#create',
... ... @@ -34,7 +37,7 @@ module Merit
34 37 value: 1,
35 38 description: _('Article community of a comment'),
36 39 default_weight: 50,
37   - condition: lambda {|target| target.profile.community? }
  40 + condition: lambda {|comment, profile| comment.profile.community? and comment.profile == profile }
38 41 },
39 42 article_author: {
40 43 action: 'article#create',
... ... @@ -42,7 +45,8 @@ module Merit
42 45 to: :author,
43 46 value: 1,
44 47 description: _('Article author'),
45   - default_weight: 500
  48 + default_weight: 500,
  49 + condition: lambda {|article, profile| article.profile == profile},
46 50 },
47 51 article_community: {
48 52 action: 'article#create',
... ... @@ -51,7 +55,7 @@ module Merit
51 55 value: 1,
52 56 description: _('Article community'),
53 57 default_weight: 600,
54   - condition: lambda {|target| target.profile.community? }
  58 + condition: lambda {|article, profile| article.profile.community? and article.profile == profile }
55 59 },
56 60 vote_voteable_author: {
57 61 action: 'vote#create',
... ... @@ -61,6 +65,7 @@ module Merit
61 65 value: lambda {|vote| vote.vote},
62 66 description: _('Author of a voted content'),
63 67 default_weight: 50,
  68 + condition: lambda {|vote, profile| vote.voteable.profile == profile }
64 69 },
65 70 vote_voteable: {
66 71 action: 'vote#create',
... ... @@ -69,7 +74,8 @@ module Merit
69 74 profile: lambda {|vote| vote.voteable.profile},
70 75 value: lambda {|vote| vote.vote},
71 76 description: _('Voted content'),
72   - default_weight: 50
  77 + default_weight: 50,
  78 + condition: lambda {|vote, profile| vote.voteable.profile == profile }
73 79 },
74 80 vote_voter: {
75 81 action: 'vote#create',
... ... @@ -77,7 +83,9 @@ module Merit
77 83 to: lambda {|vote| vote.voter},
78 84 value: lambda {|vote| 1},
79 85 description: _('Voter'),
80   - default_weight: 10
  86 + default_weight: 10,
  87 + condition: lambda {|vote, profile| vote.voteable.profile == profile }
  88 +
81 89 },
82 90 friends: {
83 91 action: 'friendship#create',
... ... @@ -85,7 +93,8 @@ module Merit
85 93 to: lambda {|friendship| friendship.person},
86 94 value: 1,
87 95 description: _('Friends'),
88   - default_weight: 5
  96 + default_weight: 5,
  97 + profile_action: false
89 98 },
90 99 profile_completion: {
91 100 action: ['account#create', 'account#update'],
... ... @@ -93,35 +102,42 @@ module Merit
93 102 to: lambda {|user| user.person},
94 103 value: 1,
95 104 description: _('Profile Completion'),
96   - default_weight: 5,
  105 + default_weight: 100,
97 106 model_name: "User",
98   - condition: lambda {|user| user.person.profile_completion_score_condition }
  107 + condition: lambda {|user| user.person.profile_completion_score_condition },
  108 + profile_action: false
99 109 }
100 110 }
101 111  
102   - def weight(category)
103   - settings = Noosfero::Plugin::Settings.new(@environment, GamificationPlugin)
104   - settings.settings.fetch(:point_rules, {}).fetch(category.to_s, {}).fetch('weight', AVAILABLE_RULES[category][:default_weight]).to_i
105   - end
106   -
107   - def calculate_score(target, category, value)
  112 + def calculate_score(target, weight, value)
108 113 value = value.call(target) if value.respond_to?(:call)
109   - weight(category) * value
  114 + weight * value
110 115 end
111 116  
112   - def condition(setting, target)
  117 + def condition(setting, target, profile)
113 118 condition = setting[:condition]
114   - condition.present? ? condition.call(target) : true
  119 + if condition.present?
  120 + if setting.fetch(:profile_action, true)
  121 + condition.call(target, profile)
  122 + else
  123 + condition.call(target)
  124 + end
  125 + else
  126 + true
  127 + end
115 128 end
116 129  
117 130 def initialize(environment=nil)
118   - return if environment.nil?
  131 + #return if environment.nil?
119 132 @environment = environment
  133 + @environment = Environment.default if environment.nil?
120 134  
121   - AVAILABLE_RULES.each do |category, setting|
122   - [setting[:action], setting[:undo_action]].compact.zip([1, -1]).each do |action, signal|
123   - score lambda {|target| signal * calculate_score(target, category, setting[:value])}, on: action, to: setting[:to], category: category do |target|
124   - condition(setting, target)
  135 + AVAILABLE_RULES.each do |point_type, setting|
  136 + GamificationPlugin::PointsCategorization.by_type(point_type).includes(:profile).each do |categorization|
  137 + [setting[:action], setting[:undo_action]].compact.zip([1, -1]).each do |action, signal|
  138 + score lambda {|target| signal * calculate_score(target, categorization.weight, setting[:value])}, on: action, to: setting[:to], category: categorization.id.to_s do |target|
  139 + condition(setting, target, categorization.profile)
  140 + end
125 141 end
126 142 end
127 143 end
... ...
models/gamification_plugin/points_categorization.rb 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +class GamificationPlugin::PointsCategorization < Noosfero::Plugin::ActiveRecord
  2 + belongs_to :profile
  3 + belongs_to :point_type, class_name: 'GamificationPlugin::PointsType', foreign_key: :point_type_id, dependent: :destroy
  4 + attr_accessible :profile_id, :profile, :point_type_id, :weight
  5 +
  6 + validates_presence_of :point_type_id, :weight
  7 +
  8 + scope :by_type, lambda { |p_type| joins(:point_type).where(gamification_plugin_points_types: {name: p_type}) }
  9 + scope :by_profile, lambda { |p_profile| joins(:profile).where(profiles: {identifier: p_profile}) }
  10 +end
... ...
models/gamification_plugin/points_type.rb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +class GamificationPlugin::PointsType < Noosfero::Plugin::ActiveRecord
  2 + attr_accessible :description, :name
  3 +
  4 + validates_presence_of :name
  5 +end
... ...
test/test_helper.rb
1 1 require_relative "../../../test/test_helper"
  2 +
  3 +def create_merit_categorization
  4 + c = fast_create(Community)
  5 + Merit::PointRules::AVAILABLE_RULES.each do |name, setting|
  6 + point_type = GamificationPlugin::PointsType.find_by_name name
  7 + point_type = GamificationPlugin::PointsType.create name: name, description: setting['description'] if point_type.nil?
  8 + profile = setting.fetch(:profile_action, true) ? c : nil
  9 + GamificationPlugin::PointsCategorization.create! point_type_id: point_type.id, profile: profile, weight: setting[:default_weight]
  10 + end
  11 + c
  12 +end#
... ...
test/unit/article_test.rb
... ... @@ -5,19 +5,20 @@ class ArticleTest &lt; ActiveSupport::TestCase
5 5 def setup
6 6 @person = create_user('testuser').person
7 7 @environment = Environment.default
  8 + @community = create_merit_categorization
8 9 GamificationPlugin.gamification_set_rules(@environment)
9 10 end
10 11  
11   - attr_accessor :person, :environment
  12 + attr_accessor :person, :environment, :community
12 13  
13 14 should 'add merit points to author when create a new article' do
14   - create(TextArticle, :profile_id => person.id, :author => person)
  15 + create(TextArticle, :profile_id => community.id, :author => person)
15 16 assert_equal 1, person.score_points.count
16 17 assert person.score_points.first.action.present?
17 18 end
18 19  
19 20 should 'subtract merit points to author when destroy an article' do
20   - article = create(TextArticle, :profile_id => person.id, :author => person)
  21 + article = create(TextArticle, :profile_id => @community.id, :author => person)
21 22 assert_equal 1, person.score_points.count
22 23 article.destroy
23 24 assert_equal 2, person.score_points.count
... ... @@ -43,42 +44,45 @@ class ArticleTest &lt; ActiveSupport::TestCase
43 44 assert_equal [1, 2], person.badges.map(&:level)
44 45 end
45 46  
46   - should 'add merit points to article owner when an user like it' do
47   - article = create(TextArticle, :name => 'Test', :profile => person, :author => person)
  47 + should 'add merit points to community article owner when an user like it' do
  48 + article = create(TextArticle, :name => 'Test', :profile => @community, :author => person)
48 49  
49   - assert_difference 'article.author.points(:category => :vote_voteable_author)', 50 do
  50 + c = GamificationPlugin::PointsCategorization.by_type(:vote_voteable_author).where(profile_id: @community.id).first
  51 + assert_difference 'article.author.points(:category => c.id.to_s)', c.weight do
50 52 Vote.create!(:voter => person, :voteable => article, :vote => 1)
51 53 end
52 54 end
53 55  
54 56 should 'add merit points to article when an user like it' do
55   - article = create(TextArticle, :name => 'Test', :profile => person, :author => person)
  57 + article = create(TextArticle, :name => 'Test', :profile => @community, :author => person)
56 58 article = article.reload
57 59  
58   - assert_difference 'article.points(:category => :vote_voteable)', 50 do
  60 + c = GamificationPlugin::PointsCategorization.by_type(:vote_voteable).where(profile_id: @community.id).first
  61 + assert_difference 'article.points(:category => c.id.to_s)', c.weight do
59 62 Vote.create!(:voter => person, :voteable => article, :vote => 1)
60 63 end
61 64 end
62 65  
63 66 should 'add merit points to community when create a new article' do
64   - community = fast_create(Community)
65 67 assert_difference 'community.score_points.count' do
66   - create(TextArticle, :profile_id => community.id, :author => person)
  68 + create(TextArticle, :profile_id => @community.id, :author => person)
67 69 end
68 70 end
69 71  
70 72 should 'add merit points to voter when he likes an article' do
71   - article = create(TextArticle, :name => 'Test', :profile => person, :author => person)
  73 + article = create(TextArticle, :name => 'Test', :profile => @community, :author => person)
72 74  
73   - assert_difference 'article.author.points(:category => :vote_voter)', 10 do
  75 + c = GamificationPlugin::PointsCategorization.by_type(:vote_voter).where(profile_id: @community.id).first
  76 + assert_difference 'article.author.points(:category => c.id.to_s)', c.weight do
74 77 Vote.create!(:voter => person, :voteable => article, :vote => 1)
75 78 end
76 79 end
77 80  
78 81 should 'add merit points to voter when he dislikes an article' do
79   - article = create(TextArticle, :name => 'Test', :profile => person, :author => person)
  82 + article = create(TextArticle, :name => 'Test', :profile => @community, :author => person)
80 83  
81   - assert_difference 'article.author.points(:category => :vote_voter)', 10 do
  84 + c = GamificationPlugin::PointsCategorization.by_type(:vote_voter).where(profile_id: @community.id).first
  85 + assert_difference 'article.author.points(:category => c.id.to_s)', c.weight do
82 86 Vote.create!(:voter => person, :voteable => article, :vote => -1)
83 87 end
84 88 end
... ...
test/unit/comment_test.rb
... ... @@ -5,11 +5,12 @@ class CommentTest &lt; ActiveSupport::TestCase
5 5 def setup
6 6 @person = create_user('testuser').person
7 7 @author = create_user('testauthoruser').person
8   - @article = create(TextileArticle, :profile_id => person.id, :author_id => @author.id)
  8 + @community = create_merit_categorization
  9 + @article = create(TextileArticle, :profile_id => @community.id, :author_id => @author.id)
9 10 @environment = Environment.default
10 11 GamificationPlugin.gamification_set_rules(@environment)
11 12 end
12   - attr_accessor :person, :article, :environment, :author
  13 + attr_accessor :person, :article, :environment, :author, :community
13 14  
14 15 should 'add merit points to author when create a new comment' do
15 16 create(Comment, :source => article, :author_id => person.id)
... ... @@ -67,7 +68,8 @@ class CommentTest &lt; ActiveSupport::TestCase
67 68 should 'add merit points to comment owner when an user like his comment' do
68 69 comment = create(Comment, :source => article, :author_id => person.id)
69 70  
70   - assert_difference 'comment.author.points(:category => :vote_voteable_author)', 50 do
  71 + c = GamificationPlugin::PointsCategorization.by_type(:vote_voteable_author).where(profile_id: article.profile.id).first
  72 + assert_difference 'comment.author.points(:category => c.id.to_s)', c.weight do
71 73 Vote.create!(:voter => person, :voteable => comment, :vote => 1)
72 74 end
73 75 end
... ... @@ -84,7 +86,8 @@ class CommentTest &lt; ActiveSupport::TestCase
84 86 should 'subtract merit points from comment owner when an user dislike his comment' do
85 87 comment = create(Comment, :source => article, :author_id => person.id)
86 88  
87   - assert_difference 'comment.author.points(:category => :vote_voteable_author)', -50 do
  89 + c = GamificationPlugin::PointsCategorization.by_type(:vote_voteable_author).where(profile_id: article.profile.id).first
  90 + assert_difference 'comment.author.points(:category => c.id.to_s)', -1*c.weight do
88 91 Vote.create!(:voter => person, :voteable => comment, :vote => -1)
89 92 end
90 93 end
... ... @@ -107,7 +110,8 @@ class CommentTest &lt; ActiveSupport::TestCase
107 110 should 'add merit points to voter when he likes a comment' do
108 111 comment = create(Comment, :source => article, :author_id => person.id)
109 112  
110   - assert_difference 'comment.author.points(:category => :vote_voter)', 10 do
  113 + c = GamificationPlugin::PointsCategorization.by_type(:vote_voter).where(profile_id: community.id).first
  114 + assert_difference 'comment.author.points(:category => c.id.to_s)', c.weight do
111 115 Vote.create!(:voter => person, :voteable => comment, :vote => 1)
112 116 end
113 117 end
... ... @@ -115,21 +119,24 @@ class CommentTest &lt; ActiveSupport::TestCase
115 119 should 'add merit points to voter when he dislikes a comment' do
116 120 comment = create(Comment, :source => article, :author_id => person.id)
117 121  
118   - assert_difference 'comment.author.points(:category => :vote_voter)', 10 do
  122 + c = GamificationPlugin::PointsCategorization.by_type(:vote_voter).where(profile_id: community.id).first
  123 + assert_difference 'comment.author.points(:category => c.id.to_s)', c.weight do
119 124 Vote.create!(:voter => person, :voteable => comment, :vote => -1)
120 125 end
121 126 end
122 127  
123 128 should 'add merit points to source article when create a comment' do
124   - assert_difference 'article.points(:category => :comment_article)', 50 do
  129 + c = GamificationPlugin::PointsCategorization.by_type(:comment_article).where(profile_id: community.id).first
  130 + assert_difference 'article.points(:category => c.id.to_s)', c.weight do
125 131 create(Comment, :source => article, :author_id => person.id)
126 132 end
127 133 end
128 134  
129 135 should 'add merit points to source community when create a comment' do
130   - community = fast_create(Community)
131   - article = create(TextileArticle, :profile_id => community.id, :author_id => @author.id)
132   - assert_difference 'community.points(:category => :comment_community)', 50 do
  136 + article = create(TextileArticle, :profile_id => community.id, :author_id => author.id)
  137 +
  138 + c = GamificationPlugin::PointsCategorization.by_type(:comment_community).where(profile_id: community.id).first
  139 + assert_difference 'community.points(:category => c.id.to_s)', c.weight do
133 140 create(Comment, :source => article, :author_id => person.id)
134 141 end
135 142 end
... ...
test/unit/gamification_plugin_points_categorizations_test.rb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +require 'test_helper'
  2 +
  3 +class GamificationPluginPointsCategorizationsTest < ActiveSupport::TestCase
  4 + # test "the truth" do
  5 + # assert true
  6 + # end
  7 +end
... ...
test/unit/gamification_plugin_points_types_test.rb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +require 'test_helper'
  2 +
  3 +class GamificationPluginPointsTypesTest < ActiveSupport::TestCase
  4 + # test "the truth" do
  5 + # assert true
  6 + # end
  7 +end
... ...
test/unit/person_test.rb
... ... @@ -4,6 +4,7 @@ class PersonTest &lt; ActiveSupport::TestCase
4 4  
5 5 def setup
6 6 @environment = Environment.default
  7 + create_merit_categorization
7 8 GamificationPlugin.gamification_set_rules(@environment)
8 9 @person = create_user('testuser').person
9 10 end
... ... @@ -30,7 +31,8 @@ class PersonTest &lt; ActiveSupport::TestCase
30 31 should 'add points when add someone as a friendly' do
31 32 other_person = create_user("testuserfriend").person
32 33 person.add_friend(other_person)
33   - assert_equal 5, person.score_points(:category => :friends).sum(:num_points)
  34 + c = GamificationPlugin::PointsCategorization.by_type(:friends).first
  35 + assert_equal 5, person.score_points(:category => c.id.to_s).sum(:num_points)
34 36 end
35 37  
36 38 end
... ...
test/unit/point_rules_test.rb
... ... @@ -4,31 +4,19 @@ class PointRulesTest &lt; ActiveSupport::TestCase
4 4  
5 5 def setup
6 6 @environment = Environment.default
  7 + create_merit_categorization
7 8 @point_rules = Merit::PointRules.new(@environment)
8 9 end
9 10  
10 11 attr_accessor :environment, :point_rules
11 12  
12   - should 'not define rules when environment is nil' do
13   - point_rules = Merit::PointRules.new
14   - assert point_rules.defined_rules.blank?
15   - end
  13 + #should 'not define rules when environment is nil' do
  14 + #point_rules = Merit::PointRules.new
  15 + #assert point_rules.defined_rules.blank?
  16 + #end
16 17  
17 18 should 'define rules when environment is present' do
18 19 assert point_rules.defined_rules.present?
19 20 end
20 21  
21   - should 'weight returns the default value when value is not setted in environment' do
22   - Merit::PointRules::AVAILABLE_RULES.each do |category, setting|
23   - assert_equal setting[:default_weight], point_rules.weight(category)
24   - end
25   - end
26   -
27   - should 'weight returns value from environment when it is setted' do
28   - settings = Noosfero::Plugin::Settings.new(environment, GamificationPlugin, {})
29   - settings.set_setting(:point_rules, {'comment_author' => {'weight' => '500'}})
30   - settings.save!
31   - assert_equal 500, point_rules.weight(:comment_author)
32   - end
33   -
34 22 end
... ...
test/unit/profile_test.rb
... ... @@ -45,10 +45,12 @@ class ProfileTest &lt; ActiveSupport::TestCase
45 45 end
46 46  
47 47 should 'update profile level when the score changes' do
  48 + community = create_merit_categorization
48 49 GamificationPlugin.gamification_set_rules(environment)
  50 +
49 51 person = create_user('testuser').person
50 52 assert_equal 0, person.level
51   - create(TextArticle, :profile_id => profile.id, :author => person)
  53 + create(TextArticle, :profile_id => community.id, :author => person)
52 54 assert_equal 3, person.reload.level
53 55 end
54 56  
... ...