Commit e8437fd41151d0213bcb6ce142d127480c5ffd6c

Authored by Victor Costa
2 parents 4fa3b984 4d4afd7e

Merge branch 'master' of softwarepublico.gov.br:noosfero-plugins/gamification

script/check_merit_actions_vs_points.rb 0 → 100644
... ... @@ -0,0 +1,161 @@
  1 +#!/usr/bin/env ruby
  2 +# encoding: UTF-8
  3 +
  4 +#
  5 +# This script was created for ensuring all the actions observed
  6 +# by merit for pontuation was judged and pontuated accordingly
  7 +# It checks the merit_actions registers for each action(model
  8 +# create or destroy) and recreates it
  9 +#
  10 +
  11 +require 'csv'
  12 +
  13 +class ProcessObserver
  14 + def update(changed_data)
  15 + merit = changed_data[:merit_object]
  16 + if merit.kind_of?(Merit::Score::Point)
  17 + action = Merit::Action.find(changed_data[:merit_action_id])
  18 + new_date = YAML.load(action.target_data).created_at
  19 + action.update_attribute(:created_at, new_date)
  20 + merit.update_attribute(:created_at, new_date)
  21 + end
  22 + end
  23 +end
  24 +
  25 +def create_action(obj, index, count)
  26 + target_model = obj.class.base_class.name.downcase
  27 + action = Merit::Action.find_by_target_id_and_target_model_and_action_method(obj.id, target_model, 'create')
  28 + if action.nil?
  29 + puts "#{index}/#{count} Create merit action for #{target_model} #{obj.id}"
  30 + begin
  31 + obj.new_merit_action(:create)
  32 + rescue Exception => e
  33 + puts "Could not be create: #{e.message}"
  34 + end
  35 + end
  36 +end
  37 +
  38 +def recreate_actions person, objects, category
  39 + puts "Recreating actions for #{person.identifier} on model #{objects.first.class.base_class.name}"
  40 + actions = Merit::Action.where(target_id: objects, target_model: objects.first.class.base_class.name.downcase, action_method: 'create')
  41 + Merit::Score::Point.where(action_id: actions).destroy_all
  42 + actions.destroy_all
  43 + # erase remaining points if any (can be wrong on destroy cases ?)
  44 + person.score_points.where(score_id: Merit::Score.where(category: category)).destroy_all
  45 + count = objects.count
  46 + objects.each_with_index{ |obj, index| create_action(obj, index, count) }
  47 +end
  48 +
  49 +def calc_points categorization, objects
  50 + rule = Merit::PointRules::AVAILABLE_RULES[categorization.point_type.name.to_sym]
  51 + return 0 if rule.nil?
  52 +
  53 + sum = objects.map{|o| rule[:value].respond_to?(:call) ? rule[:value].call(o) : rule[:value] }.sum
  54 + return sum * categorization.weight
  55 +end
  56 +
  57 +# avoid updating level on every action for increasing performance
  58 +Merit.observers.delete('RankObserver')
  59 +
  60 +Merit.observers << 'ProcessObserver'
  61 +
  62 +class Article < ActiveRecord::Base
  63 + def self.text_article_types
  64 + ['ProposalsDiscussionPlugin::Proposal']
  65 + end
  66 +end
  67 +
  68 +puts "Creaning up points from actions which don't exist"
  69 +Merit::Score::Point.includes(:action).find_each(batch_size: 100) do |point|
  70 + point.destroy if point.action.nil?
  71 +end
  72 +
  73 +Environment.all.each do |environment|
  74 + puts "Process environment #{environment.name}"
  75 +
  76 + Merit::AppPointRules.clear
  77 + Merit::AppBadgeRules.clear
  78 + Merit::AppPointRules.merge!(Merit::PointRules.new(environment).defined_rules)
  79 + Merit::AppBadgeRules.merge!(Merit::BadgeRules.new(environment).defined_rules)
  80 +
  81 + group_control = YAML.load(File.read(File.join(Rails.root,'tmp','control_group.yml'))) if File.exist?(File.join(Rails.root,'tmp','control_group.yml'))
  82 + conditions = group_control.nil? ? {} : {:identifier => group_control.map{|k,v| v['profiles']}.flatten}
  83 + people_count = environment.people.where(conditions).count
  84 + person_index = 0
  85 + remaining_wrong_points = []
  86 + puts "Analising environment people"
  87 + environment.people.find_each(:conditions => conditions) do |person|
  88 + person_index += 1
  89 + profile_ids = GamificationPlugin::PointsCategorization.uniq.pluck(:profile_id)
  90 + profile_ids.keep_if { |item| group_control.keys.include?(item) } unless group_control.nil?
  91 + profile_ids.each do |profile_id|
  92 + profile = Profile.where(id: profile_id).first
  93 + if profile.nil?
  94 + profile_name = 'generic'
  95 + # person actions
  96 + person_articles = Article.where(author_id: person.id)
  97 + comments = Comment.where(author_id: person.id)
  98 + votes = Vote.for_voter(person)
  99 + follows = ArticleFollower.where(person_id: person.id)
  100 + else
  101 + profile_name = profile.identifier
  102 + #person actions
  103 + person_articles = Article.where(author_id: person.id, profile_id: profile)
  104 + comments = Comment.where(author_id: person.id, source_id: profile.articles)
  105 + general_votes = Vote.for_voter(person)
  106 + votes = general_votes.where("(voteable_type = 'Article' and voteable_id in (?)) or (voteable_type = 'Comment' and voteable_id in (?))",profile.articles, Comment.where(source_type: "Article", source_id: profile.articles))
  107 + follows = ArticleFollower.where(person_id: person.id, article_id: profile.articles)
  108 + end
  109 + # received actions
  110 + comments_received = Comment.where(:source_id => person_articles)
  111 + votes_received = Vote.where("(voteable_type = 'Article' and voteable_id in (?)) or (voteable_type = 'Comment' and voteable_id in (?))",person_articles, person.comments)
  112 + follows_received = ArticleFollower.where(:article_id => person_articles)
  113 +
  114 + puts "#{person_index}/#{people_count} - Analising points for #{person.identifier} on #{profile_name}"
  115 + puts "Proposed #{person_articles.count} times, Commented #{comments.count} times, Voted #{votes.count} times, Followed #{follows.count} times"
  116 + puts "Received #{votes_received.count} votes, #{comments_received.count} comments, #{follows_received.count} follows\n"
  117 +
  118 + scope_by_type = {
  119 + article_author: person_articles, comment_author: comments, vote_voter: votes, follower: follows,
  120 + comment_article_author: comments_received, vote_voteable_author: votes_received, followed_article_author: follows_received
  121 + }
  122 +
  123 + puts "Points:"
  124 + scope_by_type.each do |type, scope|
  125 + c = GamificationPlugin::PointsCategorization.for_type(type).where(profile_id: profile_id).joins(:point_type).first
  126 + points = calc_points c, scope
  127 + puts "On #{c.point_type.name} it should have #{points} and have #{person.points(category: c.id.to_s)} "
  128 + if points != person.points(category: c.id.to_s)
  129 + recreate_actions person, scope, c.id.to_s
  130 + points = calc_points c, scope
  131 + puts "after recreating points the person has: #{person.reload.points(category: c.id.to_s)} and should have #{points}"
  132 + remaining_wrong_points << [person.identifier, person.name, scope.first.class.base_class.name, profile_name, c.id, c.point_type.name, scope.count*c.weight, person.points(category: c.id.to_s)] if points != person.points(category: c.id.to_s)
  133 + end
  134 + end
  135 + puts
  136 + end
  137 + end
  138 +
  139 + # update everyone's level after the whole pontuation,
  140 + # which is much faster than on every created action
  141 + environment.people.find_each(batch_size: 100) do |person|
  142 + puts "Updating #{person.identifier} level\n"
  143 + person.update_attribute(:level, person.gamification_plugin_calculate_level)
  144 + end
  145 +
  146 + # write to the spreadsheet the person points that couldn't regulate
  147 + unless remaining_wrong_points.blank?
  148 + CSV.open( "gamification_points_out_expectation.csv", 'w' ) do |csv|
  149 + csv << ['identifier', 'name', 'action', 'profile', 'category id', 'category type', 'should have', 'have']
  150 + remaining_wrong_points.each do |line|
  151 + csv << line
  152 + end
  153 + end
  154 + end
  155 +
  156 + if remaining_wrong_points.count
  157 + puts "Finished. There was #{remaining_wrong_points.count} people/pontuation types with errors after check and fix. Please check the created spreadsheet."
  158 + else
  159 + puts "Finished. There was no errors after checking. \o/ Pontuation seems to be ok!"
  160 + end
  161 +end
... ...
script/export_ranking.rb
... ... @@ -37,15 +37,18 @@ profile_ids.each do |profile_id|
37 37 person_down_votes = person.comments.joins(:votes).where('vote < 0').count + person_articles.joins(:votes).where('vote < 0').count
38 38 person_comments = person.comments.count
39 39 person_followers = (person.following_articles & person.article_followers.where(article_id: person_articles)).count
  40 + votes = Vote.for_voter(person).count
40 41 else
41 42 person_articles = profile.articles.where(:author_id => person.id)
42 43 person_up_votes = person.comments.where(:source_id => profile.articles).joins(:votes).where('vote > 0').count + person_articles.joins(:votes).where('vote > 0').count
43 44 person_down_votes = person.comments.where(:source_id => profile.articles).joins(:votes).where('vote < 0').count + person_articles.joins(:votes).where('vote < 0').count
44 45 person_comments = person.comments.where(:source_id => profile.articles).count
45 46 person_followers = (person.following_articles & person.article_followers.where(article_id: profile.articles)).count
  47 + the_votes = Vote.for_voter(person)
  48 + votes = the_votes.where(voteable_type: 'Article', voteable_id: profile.articles).count + the_votes.where(voteable_type: 'Comment', voteable_id: Comment.where(source_type: ["ProposalsDiscussionPlugin::Proposal", "Article"], source_id: profile.articles)).count
46 49 end
47 50 quantities_values = [
48   - Vote.for_voter(person).count,
  51 + votes,
49 52 person.friends.count,
50 53 person_up_votes,
51 54 person_down_votes,
... ...
script/process_merit_rules.rb
... ... @@ -35,13 +35,16 @@ end
35 35 # person.sash.destroy unless person.sash.nil?
36 36 #end
37 37  
  38 +# avoid updating level on every action for increasing performance
  39 +Merit.observers.delete('RankObserver')
  40 +
38 41 Merit.observers << 'ProcessObserver'
39 42  
40 43 class Article < ActiveRecord::Base
41 44 def self.text_article_types
42 45 # ['TextArticle', 'TextileArticle', 'TinyMceArticle', 'ProposalsDiscussionPlugin::Proposal']
43 46 ['ProposalsDiscussionPlugin::Proposal']
44   - end
  47 + end
45 48 end
46 49  
47 50 Environment.all.each do |environment|
... ... @@ -56,7 +59,7 @@ Environment.all.each do |environment|
56 59 article_index = 0
57 60  
58 61 puts "Amount of articles '#{article_count}'"
59   - environment.articles.where(:type => Article.text_article_types).find_each do |article|
  62 + environment.articles.includes(:comments).where(:type => Article.text_article_types).find_each(batch_size: 100) do |article|
60 63 article_index += 1
61 64 puts "Analising article #{article_index} of #{article_count}"
62 65 create_action(article, article_index, article_count)
... ...