From d3025fcfb3df15cc3fdd0fb86486895995f5da6f Mon Sep 17 00:00:00 2001 From: Hugo Melo Date: Sat, 26 Sep 2015 11:44:46 -0300 Subject: [PATCH] Adapt gamification to profile categorization --- db/migrate/20150924010919_create_gamification_plugin_points_types.rb | 10 ++++++++++ db/migrate/20150924011409_create_gamification_plugin_points_categorizations.rb | 13 +++++++++++++ db/seeds.rb | 5 +++++ lib/ext/person.rb | 3 ++- lib/merit/point_rules.rb | 66 +++++++++++++++++++++++++++++++++++++++++------------------------- models/gamification_plugin/points_categorization.rb | 10 ++++++++++ models/gamification_plugin/points_type.rb | 5 +++++ test/test_helper.rb | 11 +++++++++++ test/unit/article_test.rb | 32 ++++++++++++++++++-------------- test/unit/comment_test.rb | 27 +++++++++++++++++---------- test/unit/gamification_plugin_points_categorizations_test.rb | 7 +++++++ test/unit/gamification_plugin_points_types_test.rb | 7 +++++++ test/unit/person_test.rb | 4 +++- test/unit/point_rules_test.rb | 22 +++++----------------- test/unit/profile_test.rb | 4 +++- 15 files changed, 157 insertions(+), 69 deletions(-) create mode 100644 db/migrate/20150924010919_create_gamification_plugin_points_types.rb create mode 100644 db/migrate/20150924011409_create_gamification_plugin_points_categorizations.rb create mode 100644 db/seeds.rb create mode 100644 models/gamification_plugin/points_categorization.rb create mode 100644 models/gamification_plugin/points_type.rb create mode 100644 test/unit/gamification_plugin_points_categorizations_test.rb create mode 100644 test/unit/gamification_plugin_points_types_test.rb diff --git a/db/migrate/20150924010919_create_gamification_plugin_points_types.rb b/db/migrate/20150924010919_create_gamification_plugin_points_types.rb new file mode 100644 index 0000000..cf7fb8d --- /dev/null +++ b/db/migrate/20150924010919_create_gamification_plugin_points_types.rb @@ -0,0 +1,10 @@ +class CreateGamificationPluginPointsTypes < ActiveRecord::Migration + def change + create_table :gamification_plugin_points_types do |t| + t.string :name + t.text :description + + t.timestamps + end + end +end diff --git a/db/migrate/20150924011409_create_gamification_plugin_points_categorizations.rb b/db/migrate/20150924011409_create_gamification_plugin_points_categorizations.rb new file mode 100644 index 0000000..1dbe27c --- /dev/null +++ b/db/migrate/20150924011409_create_gamification_plugin_points_categorizations.rb @@ -0,0 +1,13 @@ +class CreateGamificationPluginPointsCategorizations < ActiveRecord::Migration + def change + create_table :gamification_plugin_points_categorizations do |t| + t.references :profile + t.integer :point_type_id + t.integer :weight + + t.timestamps + end + add_index :gamification_plugin_points_categorizations, :profile_id + add_index :gamification_plugin_points_categorizations, :point_type_id, name: 'index_points_categorizations_on_point_type_id' + end +end diff --git a/db/seeds.rb b/db/seeds.rb new file mode 100644 index 0000000..4706742 --- /dev/null +++ b/db/seeds.rb @@ -0,0 +1,5 @@ +unless GamificationPlugin::PointsType.count + Merit::PointRules::AVAILABLE_RULES.each do |name , setting| + GamificationPlugin::PointsType.create! name: name.to_s, description: setting[:description] + end +end diff --git a/lib/ext/person.rb b/lib/ext/person.rb index 73dce04..c7a56c2 100644 --- a/lib/ext/person.rb +++ b/lib/ext/person.rb @@ -9,6 +9,7 @@ class Person self.points(category: 'profile_completion') == 0 and self.is_profile_complete? end def is_profile_complete? - true + # FIXME: FIND OUT A WAY TO CHECK EVERY REGISTRY FIELD + false end end diff --git a/lib/merit/point_rules.rb b/lib/merit/point_rules.rb index 2dfbde0..271f63b 100644 --- a/lib/merit/point_rules.rb +++ b/lib/merit/point_rules.rb @@ -9,7 +9,8 @@ module Merit to: :author, value: 1, description: _('Comment author'), - default_weight: 150 + default_weight: 150, + condition: lambda {|comment, profile| comment.source.profile == profile}, }, comment_article_author: { action: 'comment#create', @@ -17,7 +18,8 @@ module Merit to: lambda {|comment| comment.source.author}, value: 1, description: _('Article author of a comment'), - default_weight: 50 + default_weight: 50, + condition: lambda {|comment, profile| comment.source.profile == profile}, }, comment_article: { action: 'comment#create', @@ -25,7 +27,8 @@ module Merit to: lambda {|comment| comment.source}, value: 1, description: _('Source article of a comment'), - default_weight: 50 + default_weight: 50, + condition: lambda {|comment, profile| comment.source.profile == profile}, }, comment_community: { action: 'comment#create', @@ -34,7 +37,7 @@ module Merit value: 1, description: _('Article community of a comment'), default_weight: 50, - condition: lambda {|target| target.profile.community? } + condition: lambda {|comment, profile| comment.profile.community? and comment.profile == profile } }, article_author: { action: 'article#create', @@ -42,7 +45,8 @@ module Merit to: :author, value: 1, description: _('Article author'), - default_weight: 500 + default_weight: 500, + condition: lambda {|article, profile| article.profile == profile}, }, article_community: { action: 'article#create', @@ -51,7 +55,7 @@ module Merit value: 1, description: _('Article community'), default_weight: 600, - condition: lambda {|target| target.profile.community? } + condition: lambda {|article, profile| article.profile.community? and article.profile == profile } }, vote_voteable_author: { action: 'vote#create', @@ -61,6 +65,7 @@ module Merit value: lambda {|vote| vote.vote}, description: _('Author of a voted content'), default_weight: 50, + condition: lambda {|vote, profile| vote.voteable.profile == profile } }, vote_voteable: { action: 'vote#create', @@ -69,7 +74,8 @@ module Merit profile: lambda {|vote| vote.voteable.profile}, value: lambda {|vote| vote.vote}, description: _('Voted content'), - default_weight: 50 + default_weight: 50, + condition: lambda {|vote, profile| vote.voteable.profile == profile } }, vote_voter: { action: 'vote#create', @@ -77,7 +83,9 @@ module Merit to: lambda {|vote| vote.voter}, value: lambda {|vote| 1}, description: _('Voter'), - default_weight: 10 + default_weight: 10, + condition: lambda {|vote, profile| vote.voteable.profile == profile } + }, friends: { action: 'friendship#create', @@ -85,7 +93,8 @@ module Merit to: lambda {|friendship| friendship.person}, value: 1, description: _('Friends'), - default_weight: 5 + default_weight: 5, + profile_action: false }, profile_completion: { action: ['account#create', 'account#update'], @@ -93,35 +102,42 @@ module Merit to: lambda {|user| user.person}, value: 1, description: _('Profile Completion'), - default_weight: 5, + default_weight: 100, model_name: "User", - condition: lambda {|user| user.person.profile_completion_score_condition } + condition: lambda {|user| user.person.profile_completion_score_condition }, + profile_action: false } } - def weight(category) - settings = Noosfero::Plugin::Settings.new(@environment, GamificationPlugin) - settings.settings.fetch(:point_rules, {}).fetch(category.to_s, {}).fetch('weight', AVAILABLE_RULES[category][:default_weight]).to_i - end - - def calculate_score(target, category, value) + def calculate_score(target, weight, value) value = value.call(target) if value.respond_to?(:call) - weight(category) * value + weight * value end - def condition(setting, target) + def condition(setting, target, profile) condition = setting[:condition] - condition.present? ? condition.call(target) : true + if condition.present? + if setting.fetch(:profile_action, true) + condition.call(target, profile) + else + condition.call(target) + end + else + true + end end def initialize(environment=nil) - return if environment.nil? + #return if environment.nil? @environment = environment + @environment = Environment.default if environment.nil? - AVAILABLE_RULES.each do |category, setting| - [setting[:action], setting[:undo_action]].compact.zip([1, -1]).each do |action, signal| - score lambda {|target| signal * calculate_score(target, category, setting[:value])}, on: action, to: setting[:to], category: category do |target| - condition(setting, target) + AVAILABLE_RULES.each do |point_type, setting| + GamificationPlugin::PointsCategorization.by_type(point_type).includes(:profile).each do |categorization| + [setting[:action], setting[:undo_action]].compact.zip([1, -1]).each do |action, signal| + score lambda {|target| signal * calculate_score(target, categorization.weight, setting[:value])}, on: action, to: setting[:to], category: categorization.id.to_s do |target| + condition(setting, target, categorization.profile) + end end end end diff --git a/models/gamification_plugin/points_categorization.rb b/models/gamification_plugin/points_categorization.rb new file mode 100644 index 0000000..2963296 --- /dev/null +++ b/models/gamification_plugin/points_categorization.rb @@ -0,0 +1,10 @@ +class GamificationPlugin::PointsCategorization < Noosfero::Plugin::ActiveRecord + belongs_to :profile + belongs_to :point_type, class_name: 'GamificationPlugin::PointsType', foreign_key: :point_type_id, dependent: :destroy + attr_accessible :profile_id, :profile, :point_type_id, :weight + + validates_presence_of :point_type_id, :weight + + scope :by_type, lambda { |p_type| joins(:point_type).where(gamification_plugin_points_types: {name: p_type}) } + scope :by_profile, lambda { |p_profile| joins(:profile).where(profiles: {identifier: p_profile}) } +end diff --git a/models/gamification_plugin/points_type.rb b/models/gamification_plugin/points_type.rb new file mode 100644 index 0000000..26832c7 --- /dev/null +++ b/models/gamification_plugin/points_type.rb @@ -0,0 +1,5 @@ +class GamificationPlugin::PointsType < Noosfero::Plugin::ActiveRecord + attr_accessible :description, :name + + validates_presence_of :name +end diff --git a/test/test_helper.rb b/test/test_helper.rb index 6affe9f..143d3af 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1 +1,12 @@ require_relative "../../../test/test_helper" + +def create_merit_categorization + c = fast_create(Community) + Merit::PointRules::AVAILABLE_RULES.each do |name, setting| + point_type = GamificationPlugin::PointsType.find_by_name name + point_type = GamificationPlugin::PointsType.create name: name, description: setting['description'] if point_type.nil? + profile = setting.fetch(:profile_action, true) ? c : nil + GamificationPlugin::PointsCategorization.create! point_type_id: point_type.id, profile: profile, weight: setting[:default_weight] + end + c +end# diff --git a/test/unit/article_test.rb b/test/unit/article_test.rb index bb43614..598833a 100644 --- a/test/unit/article_test.rb +++ b/test/unit/article_test.rb @@ -5,19 +5,20 @@ class ArticleTest < ActiveSupport::TestCase def setup @person = create_user('testuser').person @environment = Environment.default + @community = create_merit_categorization GamificationPlugin.gamification_set_rules(@environment) end - attr_accessor :person, :environment + attr_accessor :person, :environment, :community should 'add merit points to author when create a new article' do - create(TextArticle, :profile_id => person.id, :author => person) + create(TextArticle, :profile_id => community.id, :author => person) assert_equal 1, person.score_points.count assert person.score_points.first.action.present? end should 'subtract merit points to author when destroy an article' do - article = create(TextArticle, :profile_id => person.id, :author => person) + article = create(TextArticle, :profile_id => @community.id, :author => person) assert_equal 1, person.score_points.count article.destroy assert_equal 2, person.score_points.count @@ -43,42 +44,45 @@ class ArticleTest < ActiveSupport::TestCase assert_equal [1, 2], person.badges.map(&:level) end - should 'add merit points to article owner when an user like it' do - article = create(TextArticle, :name => 'Test', :profile => person, :author => person) + should 'add merit points to community article owner when an user like it' do + article = create(TextArticle, :name => 'Test', :profile => @community, :author => person) - assert_difference 'article.author.points(:category => :vote_voteable_author)', 50 do + c = GamificationPlugin::PointsCategorization.by_type(:vote_voteable_author).where(profile_id: @community.id).first + assert_difference 'article.author.points(:category => c.id.to_s)', c.weight do Vote.create!(:voter => person, :voteable => article, :vote => 1) end end should 'add merit points to article when an user like it' do - article = create(TextArticle, :name => 'Test', :profile => person, :author => person) + article = create(TextArticle, :name => 'Test', :profile => @community, :author => person) article = article.reload - assert_difference 'article.points(:category => :vote_voteable)', 50 do + c = GamificationPlugin::PointsCategorization.by_type(:vote_voteable).where(profile_id: @community.id).first + assert_difference 'article.points(:category => c.id.to_s)', c.weight do Vote.create!(:voter => person, :voteable => article, :vote => 1) end end should 'add merit points to community when create a new article' do - community = fast_create(Community) assert_difference 'community.score_points.count' do - create(TextArticle, :profile_id => community.id, :author => person) + create(TextArticle, :profile_id => @community.id, :author => person) end end should 'add merit points to voter when he likes an article' do - article = create(TextArticle, :name => 'Test', :profile => person, :author => person) + article = create(TextArticle, :name => 'Test', :profile => @community, :author => person) - assert_difference 'article.author.points(:category => :vote_voter)', 10 do + c = GamificationPlugin::PointsCategorization.by_type(:vote_voter).where(profile_id: @community.id).first + assert_difference 'article.author.points(:category => c.id.to_s)', c.weight do Vote.create!(:voter => person, :voteable => article, :vote => 1) end end should 'add merit points to voter when he dislikes an article' do - article = create(TextArticle, :name => 'Test', :profile => person, :author => person) + article = create(TextArticle, :name => 'Test', :profile => @community, :author => person) - assert_difference 'article.author.points(:category => :vote_voter)', 10 do + c = GamificationPlugin::PointsCategorization.by_type(:vote_voter).where(profile_id: @community.id).first + assert_difference 'article.author.points(:category => c.id.to_s)', c.weight do Vote.create!(:voter => person, :voteable => article, :vote => -1) end end diff --git a/test/unit/comment_test.rb b/test/unit/comment_test.rb index 1581a84..9872bf4 100644 --- a/test/unit/comment_test.rb +++ b/test/unit/comment_test.rb @@ -5,11 +5,12 @@ class CommentTest < ActiveSupport::TestCase def setup @person = create_user('testuser').person @author = create_user('testauthoruser').person - @article = create(TextileArticle, :profile_id => person.id, :author_id => @author.id) + @community = create_merit_categorization + @article = create(TextileArticle, :profile_id => @community.id, :author_id => @author.id) @environment = Environment.default GamificationPlugin.gamification_set_rules(@environment) end - attr_accessor :person, :article, :environment, :author + attr_accessor :person, :article, :environment, :author, :community should 'add merit points to author when create a new comment' do create(Comment, :source => article, :author_id => person.id) @@ -67,7 +68,8 @@ class CommentTest < ActiveSupport::TestCase should 'add merit points to comment owner when an user like his comment' do comment = create(Comment, :source => article, :author_id => person.id) - assert_difference 'comment.author.points(:category => :vote_voteable_author)', 50 do + c = GamificationPlugin::PointsCategorization.by_type(:vote_voteable_author).where(profile_id: article.profile.id).first + assert_difference 'comment.author.points(:category => c.id.to_s)', c.weight do Vote.create!(:voter => person, :voteable => comment, :vote => 1) end end @@ -84,7 +86,8 @@ class CommentTest < ActiveSupport::TestCase should 'subtract merit points from comment owner when an user dislike his comment' do comment = create(Comment, :source => article, :author_id => person.id) - assert_difference 'comment.author.points(:category => :vote_voteable_author)', -50 do + c = GamificationPlugin::PointsCategorization.by_type(:vote_voteable_author).where(profile_id: article.profile.id).first + assert_difference 'comment.author.points(:category => c.id.to_s)', -1*c.weight do Vote.create!(:voter => person, :voteable => comment, :vote => -1) end end @@ -107,7 +110,8 @@ class CommentTest < ActiveSupport::TestCase should 'add merit points to voter when he likes a comment' do comment = create(Comment, :source => article, :author_id => person.id) - assert_difference 'comment.author.points(:category => :vote_voter)', 10 do + c = GamificationPlugin::PointsCategorization.by_type(:vote_voter).where(profile_id: community.id).first + assert_difference 'comment.author.points(:category => c.id.to_s)', c.weight do Vote.create!(:voter => person, :voteable => comment, :vote => 1) end end @@ -115,21 +119,24 @@ class CommentTest < ActiveSupport::TestCase should 'add merit points to voter when he dislikes a comment' do comment = create(Comment, :source => article, :author_id => person.id) - assert_difference 'comment.author.points(:category => :vote_voter)', 10 do + c = GamificationPlugin::PointsCategorization.by_type(:vote_voter).where(profile_id: community.id).first + assert_difference 'comment.author.points(:category => c.id.to_s)', c.weight do Vote.create!(:voter => person, :voteable => comment, :vote => -1) end end should 'add merit points to source article when create a comment' do - assert_difference 'article.points(:category => :comment_article)', 50 do + c = GamificationPlugin::PointsCategorization.by_type(:comment_article).where(profile_id: community.id).first + assert_difference 'article.points(:category => c.id.to_s)', c.weight do create(Comment, :source => article, :author_id => person.id) end end should 'add merit points to source community when create a comment' do - community = fast_create(Community) - article = create(TextileArticle, :profile_id => community.id, :author_id => @author.id) - assert_difference 'community.points(:category => :comment_community)', 50 do + article = create(TextileArticle, :profile_id => community.id, :author_id => author.id) + + c = GamificationPlugin::PointsCategorization.by_type(:comment_community).where(profile_id: community.id).first + assert_difference 'community.points(:category => c.id.to_s)', c.weight do create(Comment, :source => article, :author_id => person.id) end end diff --git a/test/unit/gamification_plugin_points_categorizations_test.rb b/test/unit/gamification_plugin_points_categorizations_test.rb new file mode 100644 index 0000000..fd7bd68 --- /dev/null +++ b/test/unit/gamification_plugin_points_categorizations_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class GamificationPluginPointsCategorizationsTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/unit/gamification_plugin_points_types_test.rb b/test/unit/gamification_plugin_points_types_test.rb new file mode 100644 index 0000000..f1a7d96 --- /dev/null +++ b/test/unit/gamification_plugin_points_types_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class GamificationPluginPointsTypesTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/unit/person_test.rb b/test/unit/person_test.rb index 0665833..a149bf3 100644 --- a/test/unit/person_test.rb +++ b/test/unit/person_test.rb @@ -4,6 +4,7 @@ class PersonTest < ActiveSupport::TestCase def setup @environment = Environment.default + create_merit_categorization GamificationPlugin.gamification_set_rules(@environment) @person = create_user('testuser').person end @@ -30,7 +31,8 @@ class PersonTest < ActiveSupport::TestCase should 'add points when add someone as a friendly' do other_person = create_user("testuserfriend").person person.add_friend(other_person) - assert_equal 5, person.score_points(:category => :friends).sum(:num_points) + c = GamificationPlugin::PointsCategorization.by_type(:friends).first + assert_equal 5, person.score_points(:category => c.id.to_s).sum(:num_points) end end diff --git a/test/unit/point_rules_test.rb b/test/unit/point_rules_test.rb index 7553195..f5ffcd7 100644 --- a/test/unit/point_rules_test.rb +++ b/test/unit/point_rules_test.rb @@ -4,31 +4,19 @@ class PointRulesTest < ActiveSupport::TestCase def setup @environment = Environment.default + create_merit_categorization @point_rules = Merit::PointRules.new(@environment) end attr_accessor :environment, :point_rules - should 'not define rules when environment is nil' do - point_rules = Merit::PointRules.new - assert point_rules.defined_rules.blank? - end + #should 'not define rules when environment is nil' do + #point_rules = Merit::PointRules.new + #assert point_rules.defined_rules.blank? + #end should 'define rules when environment is present' do assert point_rules.defined_rules.present? end - should 'weight returns the default value when value is not setted in environment' do - Merit::PointRules::AVAILABLE_RULES.each do |category, setting| - assert_equal setting[:default_weight], point_rules.weight(category) - end - end - - should 'weight returns value from environment when it is setted' do - settings = Noosfero::Plugin::Settings.new(environment, GamificationPlugin, {}) - settings.set_setting(:point_rules, {'comment_author' => {'weight' => '500'}}) - settings.save! - assert_equal 500, point_rules.weight(:comment_author) - end - end diff --git a/test/unit/profile_test.rb b/test/unit/profile_test.rb index df74ce7..1e1966b 100644 --- a/test/unit/profile_test.rb +++ b/test/unit/profile_test.rb @@ -45,10 +45,12 @@ class ProfileTest < ActiveSupport::TestCase end should 'update profile level when the score changes' do + community = create_merit_categorization GamificationPlugin.gamification_set_rules(environment) + person = create_user('testuser').person assert_equal 0, person.level - create(TextArticle, :profile_id => profile.id, :author => person) + create(TextArticle, :profile_id => community.id, :author => person) assert_equal 3, person.reload.level end -- libgit2 0.21.2