Commit d3025fcfb3df15cc3fdd0fb86486895995f5da6f
1 parent
09d49988
Exists in
master
and in
1 other branch
Adapt gamification to profile categorization
* Add point type and point categorization * Adapt point rules for new models * Adapt existing tests * Create a seeds file
Showing
15 changed files
with
157 additions
and
69 deletions
Show diff stats
db/migrate/20150924010919_create_gamification_plugin_points_types.rb
0 → 100644
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 | ... | ... |
lib/ext/person.rb
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 | ... | ... |
... | ... | @@ -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 | ... | ... |
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 < 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 < 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 < 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 < 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 < 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 < 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 < 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
test/unit/person_test.rb
... | ... | @@ -4,6 +4,7 @@ class PersonTest < 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 < 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 < 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 < 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 | ... | ... |